litellm - 💡(How to fix) Fix [Bug]: Chat Completions → Responses API bridge does not transform tool_choice named function format, causing 400 on all GPT-5.4/5.5 models [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#27611Fetched 2026-05-11 03:13:54
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Participants
Timeline (top)
labeled ×1

Error Message

litellm.BadRequestError: OpenAIException - { "error": { "message": "Unknown parameter: 'tool_choice.function'.", "type": "invalid_request_error", "param": "tool_choice.function", "code": "unknown_parameter" } }

Root Cause

In litellm/llms/openai/responses/transformation.py, the transform_responses_api_request() method (or equivalent) does not include a tool_choice normalisation step. The tools array transformation already exists (in the count_tokens path at minimum), but tool_choice is missing.

Suggested fix:

def _normalize_tool_choice_for_responses_api(tool_choice):
    """
    Chat Completions uses nested format: {"type": "function", "function": {"name": "X"}}
    Responses API uses flat format:      {"type": "function", "name": "X"}
    """
    if (
        isinstance(tool_choice, dict)
        and tool_choice.get("type") == "function"
        and "function" in tool_choice
        and isinstance(tool_choice["function"], dict)
    ):
        return {"type": "function", "name": tool_choice["function"]["name"]}
    return tool_choice

This transformation should be applied in the request transformation path before the request is forwarded to the Responses API endpoint.

Fix Action

Fix / Workaround

The only working workaround is tool_choice="required" (only viable if the caller has a single tool defined).

Code Example

400: Unknown parameter: 'tool_choice.function'.

---

import openai

client = openai.OpenAI(
    api_key="<key>",
    base_url="<litellm-proxy-url>",
)

response = client.chat.completions.create(
    model="gpt-5.4-nano",   # or gpt-5.4, gpt-5.4-mini, gpt-5.4-pro, gpt-5.5, gpt-5.5-pro
    messages=[{"role": "user", "content": "Say hello"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "Echo",
            "description": "Echo a value.",
            "strict": True,
            "parameters": {
                "type": "object",
                "additionalProperties": False,
                "properties": {"value": {"type": "string"}},
                "required": ["value"],
            },
        },
    }],
    # Standard OpenAI Python SDK format for forced named tool choice:
    tool_choice={"type": "function", "function": {"name": "Echo"}},
)

---

def _normalize_tool_choice_for_responses_api(tool_choice):
    """
    Chat Completions uses nested format: {"type": "function", "function": {"name": "X"}}
    Responses API uses flat format:      {"type": "function", "name": "X"}
    """
    if (
        isinstance(tool_choice, dict)
        and tool_choice.get("type") == "function"
        and "function" in tool_choice
        and isinstance(tool_choice["function"], dict)
    ):
        return {"type": "function", "name": tool_choice["function"]["name"]}
    return tool_choice

---

litellm.BadRequestError: OpenAIException - {
  "error": {
    "message": "Unknown parameter: 'tool_choice.function'.",
    "type": "invalid_request_error",
    "param": "tool_choice.function",
    "code": "unknown_parameter"
  }
}
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?

When calling any GPT-5.4 or GPT-5.5 model through the Chat Completions endpoint (/v1/chat/completions) with a named tool choice — i.e. tool_choice={"type": "function", "function": {"name": "MyTool"}} — LiteLLM's bridge to the Responses API passes the parameter through without transforming it, causing a 400 from OpenAI:

400: Unknown parameter: 'tool_choice.function'.

This is a schema mismatch between the two APIs:

APIFormat
Chat Completions (client sends){"type": "function", "function": {"name": "MyTool"}}
Responses API (OpenAI expects){"type": "function", "name": "MyTool"}

The tools array itself IS correctly transformed (the nested function key is flattened), but tool_choice is passed through as-is.

To make matters worse, using the Responses API flat format directly ({"type": "function", "name": "MyTool"}) is rejected by LiteLLM's input validation with a 500, because LiteLLM enforces the Chat Completions schema on ingress.

So clients using the standard OpenAI Python SDK to force a specific named tool have no working path today for these models — {"type": "function", "function": {...}} fails at OpenAI, and {"type": "function", "name": "..."} fails at LiteLLM validation.

The only working workaround is tool_choice="required" (only viable if the caller has a single tool defined).

Steps to Reproduce

import openai

client = openai.OpenAI(
    api_key="<key>",
    base_url="<litellm-proxy-url>",
)

response = client.chat.completions.create(
    model="gpt-5.4-nano",   # or gpt-5.4, gpt-5.4-mini, gpt-5.4-pro, gpt-5.5, gpt-5.5-pro
    messages=[{"role": "user", "content": "Say hello"}],
    tools=[{
        "type": "function",
        "function": {
            "name": "Echo",
            "description": "Echo a value.",
            "strict": True,
            "parameters": {
                "type": "object",
                "additionalProperties": False,
                "properties": {"value": {"type": "string"}},
                "required": ["value"],
            },
        },
    }],
    # Standard OpenAI Python SDK format for forced named tool choice:
    tool_choice={"type": "function", "function": {"name": "Echo"}},
)

Result: 400 Unknown parameter: 'tool_choice.function'.

Expected: The bridge should transform tool_choice the same way it already transforms tools, converting {"type": "function", "function": {"name": "X"}}{"type": "function", "name": "X"} before sending to the Responses API.

Affected models

All models with model_info.mode: responses:

  • gpt-5.4, gpt-5.4-mini, gpt-5.4-nano, gpt-5.4-pro
  • gpt-5.5, gpt-5.5-pro

Verified against LiteLLM v1.83.14-stable.

Root cause

In litellm/llms/openai/responses/transformation.py, the transform_responses_api_request() method (or equivalent) does not include a tool_choice normalisation step. The tools array transformation already exists (in the count_tokens path at minimum), but tool_choice is missing.

Suggested fix:

def _normalize_tool_choice_for_responses_api(tool_choice):
    """
    Chat Completions uses nested format: {"type": "function", "function": {"name": "X"}}
    Responses API uses flat format:      {"type": "function", "name": "X"}
    """
    if (
        isinstance(tool_choice, dict)
        and tool_choice.get("type") == "function"
        and "function" in tool_choice
        and isinstance(tool_choice["function"], dict)
    ):
        return {"type": "function", "name": tool_choice["function"]["name"]}
    return tool_choice

This transformation should be applied in the request transformation path before the request is forwarded to the Responses API endpoint.

Relevant log output

litellm.BadRequestError: OpenAIException - {
  "error": {
    "message": "Unknown parameter: 'tool_choice.function'.",
    "type": "invalid_request_error",
    "param": "tool_choice.function",
    "code": "unknown_parameter"
  }
}

What part of LiteLLM is this about?

Chat Completions → Responses API bridge (litellm/llms/openai/responses/transformation.py)

What LiteLLM version are you on?

v1.83.14-stable

Additional context

The {"type": "function", "function": {"name": "..."}} format is the only format the OpenAI Python SDK (≥ v1.3.9, December 2023) supports for named tool choice in Chat Completions. This means every caller using the standard SDK to force a specific tool hits this bug on Responses API-backed models.

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