litellm - 💡(How to fix) Fix [Bug]: Chat completions → Responses API bridge does not handle file_id with URL (documented LiteLLM pattern) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
BerriAI/litellm#25456Fetched 2026-04-10 03:40:59
View on GitHub
Comments
0
Participants
1
Timeline
2
Reactions
0
Participants
Timeline (top)
labeled ×2

Error Message

"error": { 3. Observe the error — the URL is passed through as input_file.file_id, which the Responses API rejects.

Root Cause

In litellm/completion_extras/litellm_responses_transformation/transformation.py, the file conversion (lines ~785-790) copies file_id from the chat completion message directly into the Responses API input_file without checking whether the value is a URL:

converted = {"type": "input_file"}
if isinstance(file_data, dict):
    for key in ["file_id", "file_data", "filename"]:
        if key in file_data:
            converted[key] = file_data[key]

The fix would be: if file_id starts with https://, use file_url instead of file_id in the converted output.

Code Example

file_url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
file_content = [
    {"type": "text", "text": "What's this file about?"},
    {"type": "file", "file": {"file_id": file_url, "format": "application/pdf"}}
]
response = completion(model="...", messages=[{"role": "user", "content": file_content}])

---

{
  "error": {
    "message": "Invalid 'input[0].content[1].file_id': 'https://arxiv.org/pdf/1706.03762'. Expected an ID that contains letters, numbers, underscores, or dashes, but this value contained additional characters.",
    "type": "invalid_request_error",
    "param": "input[0].content[1].file_id",
    "code": "invalid_value"
  }
}

---

curl -X POST "http://localhost:4000/model/new" \
  -H "Authorization: Bearer <master_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model_name": "my-responses-model",
    "litellm_params": {
      "model": "azure/gpt-4.1-mini",
      "api_base": "https://<account>.openai.azure.com/openai/v1"
    },
    "model_info": {"mode": "responses"}
  }'

---

curl -X POST "http://localhost:4000/v1/chat/completions" \
  -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "my-responses-model",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "text", "text": "What is this document about?"},
        {"type": "file", "file": {"file_id": "https://arxiv.org/pdf/1706.03762", "format": "application/pdf"}}
      ]
    }]
  }'

---

converted = {"type": "input_file"}
if isinstance(file_data, dict):
    for key in ["file_id", "file_data", "filename"]:
        if key in file_data:
            converted[key] = file_data[key]
RAW_BUFFERClick to expand / collapse

Check for existing issues

  • I have searched the existing issues and checked that my issue is not a duplicate.

What happened?

LiteLLM's document understanding documentation explicitly shows that passing an HTTPS URL in file_id is the supported way to send a file by URL through the Chat Completions API:

file_url = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"
file_content = [
    {"type": "text", "text": "What's this file about?"},
    {"type": "file", "file": {"file_id": file_url, "format": "application/pdf"}}
]
response = completion(model="...", messages=[{"role": "user", "content": file_content}])

This pattern works correctly for Gemini, Bedrock, and Anthropic bridges. However, when the model routes through the chat completions → Responses API bridge (i.e. a model with mode: "responses"), the bridge passes the URL through as input_file.file_id unchanged, which the Responses API rejects:

{
  "error": {
    "message": "Invalid 'input[0].content[1].file_id': 'https://arxiv.org/pdf/1706.03762'. Expected an ID that contains letters, numbers, underscores, or dashes, but this value contained additional characters.",
    "type": "invalid_request_error",
    "param": "input[0].content[1].file_id",
    "code": "invalid_value"
  }
}

The correct behaviour is: when file_id contains an HTTPS URL, the bridge should map it to file_url in the Responses API input_file, since file_url is the Responses API's native field for URL-based file input.

Steps to Reproduce

  1. Register an Azure model with mode: "responses":
curl -X POST "http://localhost:4000/model/new" \
  -H "Authorization: Bearer <master_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model_name": "my-responses-model",
    "litellm_params": {
      "model": "azure/gpt-4.1-mini",
      "api_base": "https://<account>.openai.azure.com/openai/v1"
    },
    "model_info": {"mode": "responses"}
  }'
  1. Send a chat completions request using the documented file_id-with-URL pattern:
curl -X POST "http://localhost:4000/v1/chat/completions" \
  -H "Authorization: Bearer <key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "my-responses-model",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "text", "text": "What is this document about?"},
        {"type": "file", "file": {"file_id": "https://arxiv.org/pdf/1706.03762", "format": "application/pdf"}}
      ]
    }]
  }'
  1. Observe the error — the URL is passed through as input_file.file_id, which the Responses API rejects.

Expected behaviour

The bridge should detect that file_id contains an HTTPS URL and map it to input_file.file_url in the Responses API request, consistent with how the Gemini bridge handles the same pattern.

Root cause

In litellm/completion_extras/litellm_responses_transformation/transformation.py, the file conversion (lines ~785-790) copies file_id from the chat completion message directly into the Responses API input_file without checking whether the value is a URL:

converted = {"type": "input_file"}
if isinstance(file_data, dict):
    for key in ["file_id", "file_data", "filename"]:
        if key in file_data:
            converted[key] = file_data[key]

The fix would be: if file_id starts with https://, use file_url instead of file_id in the converted output.

What part of LiteLLM is this about?

Proxy

What LiteLLM version are you on?

main (verified against current main branch code)

Twitter / LinkedIn details

No response

extent analysis

TL;DR

The issue can be fixed by modifying the litellm_responses_transformation to check if file_id starts with https:// and use file_url instead of file_id in the converted output.

Guidance

  • Identify the transformation.py file in litellm/completion_extras/litellm_responses_transformation/ and locate the file conversion code (lines ~785-790).
  • Modify the code to check if file_id starts with https:// and use file_url instead of file_id in the converted output.
  • Verify that the modification fixes the issue by sending a chat completions request with a URL in file_id and checking the Responses API request.
  • Test the fix with different URLs and file formats to ensure it works consistently.

Example

if isinstance(file_data, dict):
    converted = {"type": "input_file"}
    for key in ["file_id", "file_data", "filename"]:
        if key in file_data:
            if key == "file_id" and file_data[key].startswith("https://"):
                converted["file_url"] = file_data[key]
            else:
                converted[key] = file_data[key]

Notes

The fix assumes that the file_id field is the only field that needs to be modified. Additional testing may be necessary to ensure that the fix does not introduce any regressions.

Recommendation

Apply the workaround by modifying the transformation.py file to check if file_id starts with https:// and use file_url instead of file_id in the converted output. This fix should resolve the issue without requiring any additional upgrades or changes.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING