Skip to content

[BUG] LiteLLM (OpenAI/gpt-4o-mini): load_artifacts PDF attachment fails with “Invalid file data: 'file_id' … expected application/pdf, got None” #4174

Closed

Description

@kursatdinc

Describe the bug

When a user uploads a PDF and asks “what do you see in this PDF?”, ADK SaveFilesAsArtifactsPlugin saves the file as an artifact and adds an artifact placeholder. Then the agent calls the load_artifacts tool to inline/attach the artifact content. This flow works on Gemini/Vertex models, but fails on OpenAI GPT models via LiteLLM (reproduced with openai/gpt-4o-mini) with:

litellm.BadRequestError: OpenAIException - Invalid file data: 'file_id'. Expected a file with an application/pdf MIME type, but got unsupported MIME type 'None'.
This suggests the file attachment forwarded to OpenAI has missing/None MIME type for a PDF.

To Reproduce

Install dependencies:
pip install google-adk litellm openai

Run an ADK app/agent configured to use LiteLLM with model openai/gpt-4o-mini, with:
Plugin enabled: SaveFilesAsArtifactsPlugin()
Tool enabled: load_artifacts
Upload a PDF.
Send: what do you see in this pdf?
Observe that after the agent calls load_artifacts and tries to inline/attach the artifact, the request fails with:
Invalid file data: 'file_id' ... expected application/pdf ... got None
Note: The same steps succeed with Gemini and Claude models.

Expected behavior

The agent should successfully load/attach the uploaded PDF artifact and answer based on the PDF content, without failing due to missing MIME type.

Desktop (please complete the following information):

  • OS: Linux / MacOs
  • Python version(python -V): 3.10
  • ADK version(pip show google-adk): 1.22 This commit installed: Commit 43b484f

Model Information:

  • Are you using LiteLLM: Yes
  • Which model is being used: gpt-4o-mini

Notes

  • This issue was previously reported and closed as resolved, pointing to commit 89bed43, but the problem still occurs for PDF attachments when using LiteLLM + OpenAI models.
  • The failure appears to be in the artifact → file attachment step where the MIME type is lost or not set when constructing the file upload forwarded to OpenAI.
  • This only affects PDF inputs — images and other media do not reproduce the error in my tests.

Related commits

  • 89bed43 (mentioned in prior responses) — please check if this commit is related to the regression.

Additional context

Full traceback:

POST Request Sent from LiteLLM:
curl -X POST \
https://api.openai.com/v1 \
-d '{'model': 'gpt-4o-mini', 'messages': [{'role': 'system', 'content': 'You are Nova, a general-purpose root AI assistant whose primary responsibility is orchestration: detect user intent and deleral-purpose root AI assistant whose primary responsibility is orchestration: detect user integths. Your role is routing, not to produce full domain answers yourself when a suitable sub-agent existsnt and deleral-purpose root AI assistant whose primary responsibility is orchestration: detecain(s), task type, required expertise, format, and constraints (tone, length, citations, step-by-step, pt user integths. Your role is routing, not to produce full domain answers yourself when a suie step-by-step", "generate social post").\n2. Agent selection: Match the request to the single best-fit table sub-agent existsnt and deleral-purpose root AI assistant whose primary responsibility i Prefer specialized agents over yourself for domain tasks (e.g., a Botanist agent for plant ID, a High-As orchestration: detecain(s), task type, required expertise, format, and constraints (tone, l multi-faceted (e.g., research + write + fact-check), split it into sub-tasks and assign each sub-task tength, citations, step-by-step, pt user integths. Your role is routing, not to produce full d in parallel as needed.\n4. Handoff content: When delegating, send each sub-agent a concise, self-contaiomain answers yourself when a suie step-by-step", "generate social post").\n2. Agent selectioe)\n   - relevant conversation context and user preferences\n   - required output format (structure, lenn: Match the request to the single best-fit table sub-agent existsnt and delegate tasks to th\n   - explicit success criteria (what counts as a usable answer)\n5. Response assembly: After sub-agente most appropriate subordinate agent(s) base Prefer specialized agents over yourself for domansistency and obvious errors\n   - If multiple agents provided conflicting answers, run a reconciliationin tasks (e.g., a Botanist agent for plant ID, a High-Ad on their descriptionain(s), task typ- Compose a coherent, single reply to the user that indicates which sub-agent(s) provided which parts (ee, required expertise, format, and constraints (tone, l multi-faceted (e.g., research + write asked not to reveal routing.\n\nQuality, safety, and transparency\n1. Accuracy first: Ensure sub-agents + fact-check), split it into sub-tasks and assign each sub-task tength, citations, step-by-s clearly.\n2. No hallucinations: If a sub-agent output appears unsupported or speculative, request sourctep, ps and strengths. Your role is routing, not to produce full d in parallel as needed.\n4.s.\n3. Privacy & policy: Enforce user privacy and safety constraints during delegation; do not share pri Handoff content: When delegating, send each sub-agent a concise, self-contaiomain answers yo, briefly explain to the user why specific sub-agent(s) were chosen and what each will (or did) handle.\urself when a suie step-by-step", "generate social post").\n2. Agent selectioe)\n   - relevanempt one of:\n   - ask another suitable sub-agent,\n   - decompose the task differently,\n   - or providt conversation context and user preferences\n   - required output format (structure, lenn: Manswer myself or route this to a human expert”).\n6. Efficiency & user preference: Respect user preferenctch the request to the single best-fit table sub-agent exists.\n\nRouting & delegation r\n   is; if they ask for "detailed" or "step-by-step", orchestrate deeper specialist pipelines.\n\nFormatting- explicit success criteria (what counts as a usable answer)\n5. Response assembly: After subt when delegating.\n- Use structured messages for sub-agent instructions (title, user message, context, -agentules\n1. Domain detection: Parse the user\'s Prefer specialized agents over yourself foss the user requests anonymity).\n- Maintain conversational continuity: you are the orchestrator and finr domansistency and obvious errors\n   - If multiple agents provided conflicting answers, runwith a sub-agent.\n\nGoal\nAct as a reliable, domain-aware orchestrator: accurately detect intent, route a reconciliationin tasks (e.g., a Botanist agent for plant ID, a High-A request to detect do and appropriately formatted response to the user. Your default behavior is delegation — answering direcmain(s), task typ- Compose a coherent, single reply to the user that indicates which sub-agenYou are an agent. Your internal name is "NovaAgent". The description about you is "A Nova AI assistant tt(s) provided which parts (ee, required expertise, format, and constraints (tone, l multi-facate, contextually relevant, and helpful responses.".\n\nYou have a list of artifacts:\n  ["artifact_e-aeeted (e.g., research + write asked not to reveal routing.\n\nQuality, safety, and transparencifacts, you should call the\n  `load_artifacts` function to load the artifact. Always call load_artifacty\n1. Accuracy first: Ensure sub-agents + fact-check), split it into sub-tasks and assign eaccts have been loaded before. Do not depend on prior answers about the\n  artifacts.\n  '}, {'role': 'useh sub-task tength, citations, step-by-s clearly.\n2. No hallucinations: If a sub-agent outputt': '[Uploaded Artifact: "artifact_e-ae5bf877-5f86-45a8-983a-8c6257b4b62c_1"]'}, {'type': 'text', 'text' appears unsupported or speculative, request sourctep, privacy needs). Prefer explicit signaltant', 'tool_calls': [{'type': 'function', 'id': 'call_iC88a1ehmgvvztqY2vf8oQKd', 'function': {'name': 's in the user message ( in parallel as needed.\n4.s.\n3. Privacy & policy: Enforce user privab4b62c_1"]}'}}]}, {'role': 'tool', 'tool_call_id': 'call_iC88a1ehmgvvztqY2vf8oQKd', 'content': '{"artifacy and safety constraints during delegation; do not share pri Handoff content: When delegatins temporarily inserted and removed. to access these artifacts, call load_artifacts tool again."}'}, {'rog, send each sub-agent a concise, self-contaie.g., "analyze t, briefly explain to the user wha-8c6257b4b62c_1 is:'}, {'type': 'file', 'file': {'file_id': 'file-5S5XJKzYVRFSgcRRemqbCv'}}]}], 'tempery specific sub-agent(s) were chosen and what each will (or did) handle.\his paper", "solve ston': 'Loads artifacts into the session for this request.\n\nNOTE: Call when you need access to artifactsep-by-step", "generate social post").\n2. Agent selectioe)\n   - relevanempt one of:\n   - as: {'artifact_names': {'items': {'type': 'string'}, 'type': 'array'}}}}}, {'type': 'function', 'function'k another suitable sub-agent,\n   - decompose the task differently,\n   - or providt conversaP.\n    \n    Args:\n        tool_context: Tool context with state and artifact access\n        prompt: tion context and user preferences\n   - required output format (structure, lenn: Manswer mysepe_4_3")\n        num_images: Number of images to generate (default: 1)\n    \n    Returns:\n        diclf or route this to a human expert”).\n6. Efficiency & user preference: Respect user preferen'prompt': {'type': 'string'}, 'image_size': {'default': 'landscape_4_3', 'type': 'string'}, 'num_images'ctch the request to the single best-fit sub-agent by comparing the user\'s needs to each \n   True, 'stream_options': {'include_usage': True}}'


21:33:26 - LiteLLM:DEBUG: utils.py:465 - Logging Details LiteLLM-Async Success Call, cache_hit=None

Give Feedback / Get Help: https://github.com/BerriAI/litellm/issues/new
LiteLLM.Info: If you need to debug this error, use `litellm._turn_on_debug()'.

21:33:26 - LiteLLM:DEBUG: exception_mapping_utils.py:2375 - Logging Details: logger_fn - None | callable(logger_fn) - False
21:33:26 - LiteLLM:DEBUG: litellm_logging.py:2733 - Logging Details LiteLLM-Failure Call: []
2026-01-12T18:33:27.027958Z [error    ] Error in adk_events            error=litellm.BadRequestError: OpenAIException - Invalid file data: 'file_id'. Expected a file with an application/pdf MIME tystError: OpenAIException - Invalid file data: 'file_id'. Expected a file with an application/

Activity

  1. added
    models[Component] This issue is related to model support
    on Jan 16, 2026
  2. added theissue type on Jan 16, 2026
  3. kursatdinc commented on Jan 16, 2026

    @kursatdinc
    Author

    Related to #4125

  4. kursatdinc commented on Jan 16, 2026

    @kursatdinc
    Author

    Related commit: 89bed43

  5. AlejandroBlanco2001 commented on Jan 20, 2026

    @AlejandroBlanco2001

    Hey @kursatdinc, I was able to experience the same issues as you with the adk run web

  6. llalitkumarrr commented on Jan 28, 2026

    @llalitkumarrr
    Collaborator

    Hello,

    We would like to go through your code and try to reproduce this issue. Could you please share your code snippet to us reproduce this issue?

  7. kursatdinc commented on Jan 28, 2026

    @kursatdinc
    Author
    ARTIFACT_SERVICE = CustomGcsArtifactService(bucket_name="agent-artifacts-test")
    
    root_agent = Agent(
        name="NovaAgentTest",
        description="This is a test agent",
        model=LiteLlm(model="gpt-4o-mini"),
        tools=[load_artifacts],
    )
    
    app = App(
        name="AgentTest",
        root_agent=root_agent,
        plugins=[
            SaveFileAsArtifactPlugin(),
        ],
    )
    
    runner = Runner(
        app=app,
        artifact_service=ARTIFACT_SERVICE,
    )
    

    When a user uploads a PDF and asks “what do you see in this PDF?”, ADK SaveFilesAsArtifactsPlugin saves the file as an artifact and adds an artifact placeholder. Then the agent calls the load_artifacts tool to inline/attach the artifact content. This flow works on Gemini/Vertex models, but fails on OpenAI GPT models via LiteLLM (reproduced with openai/gpt-4o-mini) with:

    litellm.BadRequestError: OpenAIException - Invalid file data: 'file_id'. Expected a file with an application/pdf MIME type, but got unsupported MIME type 'None'.
    This suggests the file attachment forwarded to OpenAI has missing/None MIME type for a PDF.

  8. alejandro-sotoca-bts commented on Feb 24, 2026

    @alejandro-sotoca-bts

    Root cause and proposed fix

    I was able to reproduce this issue with adk web (not only via load_artifacts) on ADK v1.25.0 using openai/gpt-4o and azure/... models.

    Root cause

    In src/google/adk/models/lite_llm.py, the acreate_file call for OpenAI/Azure providers passes only raw bytes as the file argument:

    # Current upstream code (~line 845)
    file_response = await litellm.acreate_file(
        file=part.inline_data.data,   # <-- raw bytes only
        purpose="assistants",
        custom_llm_provider=provider,
    )

    LiteLLM (and the underlying OpenAI API) expects a multipart upload with a (filename, bytes, content_type) tuple so it can set the Content-Type header correctly. Passing only raw bytes causes the stored file to have MIME type None, which the OpenAI chat completions API then rejects.

    This code path is only reached for _FILE_ID_REQUIRED_PROVIDERS = frozenset({"openai", "azure"}). Other providers (Gemini, Anthropic) send files inline as data URIs and are not affected.

    Proposed fix

    Pass the full tuple (filename, bytes, mime_type) to acreate_file, using the file's display_name or a sensible default derived from the MIME type:

    # Add near _FILE_ID_REQUIRED_PROVIDERS
    _MIME_TO_FILENAME = {
        "application/pdf": "document.pdf",
        "application/json": "document.json",
        "application/msword": "document.doc",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "document.docx",
        "application/vnd.openxmlformats-officedocument.presentationml.presentation": "document.pptx",
        "application/x-sh": "script.sh",
    }
    
    def _filename_for_mime(mime_type: str) -> str:
        return _MIME_TO_FILENAME.get(mime_type, "document.bin")
    # In the acreate_file call
    display_name = (
        getattr(part.inline_data, "display_name", None)
        or _filename_for_mime(part.inline_data.mime_type)
    )
    file_response = await litellm.acreate_file(
        file=(
            display_name,
            part.inline_data.data,
            part.inline_data.mime_type,
        ),
        purpose="assistants",
        custom_llm_provider=provider,
    )

    This fix has been validated locally and resolves the error for PDF and other document attachments with OpenAI and Azure providers. I am happy to open a PR with this change.

  9. added 2 commits that reference this issue on Feb 24, 2026
    ecec4f0
    d072250
  10. rohityan commented on Feb 26, 2026

    @rohityan
    Collaborator

    Hi @alejandro-sotoca-bts , Your root cause analysis is spot on. Passing only raw bytes strips the metadata during the upload, which causes OpenAI to reject it with a None MIME type
    For the fix, instead of maintaining a hardcoded dictionary for MIME types, you can make it more robust by using Python's built-in mimetypes library along with the existing _normalize_mime_type helper.

  11. adk-bot commented on Mar 6, 2026

    @adk-bot
    Collaborator

    This issue has been automatically marked as stale because it has not had recent activity for 7 days after a maintainer requested clarification. It will be closed if no further activity occurs within 7 days.

  12. added
    stale[Status] Issues which have been marked inactive since there is no user response
    on Mar 6, 2026
  13. wes-evans commented on Mar 6, 2026

    @wes-evans

    I'm commenting because I'm currently experiencing this issue as well, and want to remove the stale label.

    I see #4603 is in-progress, which resolves this issue. Thanks!

  14. removed
    stale[Status] Issues which have been marked inactive since there is no user response
    on Mar 7, 2026
  15. llalitkumarrr commented on Apr 8, 2026

    @llalitkumarrr
    Collaborator

    Hello @wes-evans ,

    The PR associated with this issue is currently under review; we request your patience.

  16. added
    needs review[Status] The PR/issue is awaiting review from the maintainer
    on Apr 22, 2026
  17. added a commit that references this issue on Jul 16, 2026
    065234e
  18. added a commit that references this issue on Aug 11, 2026
    def0ee4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

models[Component] This issue is related to model supportneeds review[Status] The PR/issue is awaiting review from the maintainer

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions