Skip to content

[Bug]: Chat-to-Responses API Bridge Doesn't Transform Tool Output Content Types #17507

@alexanderkrauck

Description

@alexanderkrauck

What happened?

Bug: Chat-to-Responses API Bridge Doesn't Transform Tool Output Content Types

Summary

When using models that route through the Responses API (e.g., gpt-5.1-codex, o1-pro, o3-pro, o4-*), multimodal tool results fail with a 400 Bad Request because tool output content is passed without type transformation.

Error Message

{
  "message": {
    "content": "Error occurred: LiteLLM API error: BadRequestError: litellm.BadRequestError: {\"error\":{\"message\":\"litellm.BadRequestError: OpenAIException - {\\n  \\\"error\\\": {\\n    \\\"message\\\": \\\"Invalid value: 'text'. Supported values are: 'input_text', 'input_image', 'output_text', 'refusal', 'input_file', 'computer_screenshot', and 'summary_text'.\\\",\\n    \\\"type\\\": \\\"invalid_request_error\\\",\\n    \\\"param\\\": \\\"input[25].output[0].type\\\",\\n    \\\"code\\\": \\\"invalid_value\\\"\\n  }\\n}. Received Model Group=gpt-5.1-codex\\nAvailable Model Group Fallbacks=None\",\"type\":null,\"param\":null,\"code\":\"400\"}}",
    "role": "assistant"
  }
}

Parsed error:

  • Param: input[25].output[0].type — 26th message (tool output), first content item
  • Invalid value: 'text' (Chat Completions format)
  • Expected: 'output_text', 'input_image', etc. (Responses API format)

Root Cause & Bug Location

File: litellm/completion_extras/litellm_responses_transformation/transformation.py
Method: convert_chat_completion_messages_to_responses_api
Lines: 162-170

elif role == "tool":
    # Convert tool message to function call output format
    input_items.append(
        {
            "type": "function_call_output",
            "call_id": tool_call_id,
            "output": content,  # ❌ BUG: content passed without transformation
        }
    )

The content is passed directly without calling the existing _convert_content_to_responses_format() method.

Contrast with user/assistant messages (lines 186-196) which correctly transform content:

elif content is not None:
    input_items.append(
        {
            "type": "message",
            "role": role,
            "content": self._convert_content_to_responses_format(  # ✅ Correct
                content, cast(str, role)
            ),
        }
    )

Content Type Mapping Required

Chat Completions API Responses API
{"type": "text", "text": "..."} {"type": "output_text", "text": "..."}
{"type": "image_url", "image_url": {"url": "..."}} {"type": "input_image", "image_url": "..."}

Note: The image_url value must also be flattened from {"url": "..."} object to just the URL string.

Reproduction

import litellm

messages = [
{"role": "user", "content": "Take a screenshot"},
{
"role": "assistant",
"tool_calls": [{
"id": "call_abc123",
"type": "function",
"function": {"name": "screenshot", "arguments": "{}"}
}]
},
{
"role": "tool",
"tool_call_id": "call_abc123",
"content": [
{"type": "text", "text": "Screenshot captured"},
{"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgo..."}}
]
}
]

This fails with 400 Bad Request

response = await litellm.acompletion(
model="openai/gpt-5.1-codex", # Or any Responses API model
messages=messages
)

Suggested Fix

Transform tool output content when it's a list:

elif role == "tool":
    # Convert tool message to function call output format
    output = content
    if isinstance(content, list):
        output = self._convert_tool_output_to_responses_format(content)
    input_items.append(
        {
            "type": "function_call_output",
            "call_id": tool_call_id,
            "output": output,
        }
    )

With a new helper method:

def _convert_tool_output_to_responses_format(
    self, content: List[Dict[str, Any]]
) -> List[Dict[str, Any]]:
    """Convert tool output content types from Chat Completions to Responses API format."""
    result = []
    for item in content:
        if not isinstance(item, dict):
            result.append(item)
            continue
    item_type = item.get("type")
    if item_type == "text":
        result.append({"type": "output_text", "text": item.get("text", "")})
    elif item_type == "image_url":
        image_url = item.get("image_url")
        url = image_url.get("url") if isinstance(image_url, dict) else image_url
        result.append({"type": "input_image", "image_url": url})
    else:
        result.append(item)
return result

Alternatively, the existing _convert_content_to_responses_format method could be extended to handle tool outputs, though note that tool text outputs should use output_text (not input_text).

Affected Models

Models using OpenAI's Responses API internally:

  • gpt-5.1-codex, gpt-5.1-codex-max, codex-mini-latest
  • o1-pro, o3-pro
  • o4-* series

Related Issues

Environment

  • LiteLLM Version: Latest (Dec 2025)
  • Python: 3.11+
  • API Endpoint: /chat/completions/responses bridge

Relevant log output

Are you a ML Ops Team?

No

What LiteLLM version are you on ?

1.80.0 proxy server

Twitter / LinkedIn details

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions