BerriAI/litellm · error · ValueError

Incompatible tool choice param submitted - {tool_choice}

Error message

Incompatible tool choice param submitted - {tool_choice}

What it means

Raised while translating an Anthropic tool_choice object to the OpenAI format. The translator recognizes exactly four Anthropic tool_choice types — 'auto', 'any', 'none' and 'tool' — and maps them to OpenAI equivalents ('any' maps to 'required'). Any other value in tool_choice['type'] falls through to this ValueError.

Source

Thrown at litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py:710

        return reasoning_effort

    def translate_anthropic_tool_choice_to_openai(
        self, tool_choice: AnthropicMessagesToolChoice
    ) -> ChatCompletionToolChoiceValues:
        if tool_choice["type"] == "any":
            return "required"
        elif tool_choice["type"] == "auto":
            return "auto"
        elif tool_choice["type"] == "tool":
            # Truncate tool name if it exceeds OpenAI's 64-char limit
            original_name: Final = tool_choice.get("name", "")
            truncated_name: Final = truncate_tool_name(original_name)
            tc_function_param: Final = ChatCompletionToolChoiceFunctionParam(name=truncated_name)
            return ChatCompletionToolChoiceObjectParam(type="function", function=tc_function_param)
        elif tool_choice["type"] == "none":
            return "none"
        else:
            raise ValueError(f"Incompatible tool choice param submitted - {tool_choice}")

    def translate_anthropic_tools_to_openai(
        self, tools: list[AllAnthropicToolsValues], model: str | None = None
    ) -> tuple[list[ChatCompletionToolParam], dict[str, str]]:
        """
        Translate Anthropic tools to OpenAI format.

        Returns:
            Tuple of (translated_tools, tool_name_mapping)
            - tool_name_mapping maps truncated names back to original names
              for tools that exceeded OpenAI's 64-char limit
        """
        new_tools: Final[list[ChatCompletionToolParam]] = []
        tool_name_mapping: Final[dict[str, str]] = {}
        # "type" is the Anthropic tool type (e.g. "custom"); it must not be
        # merged into the OpenAI function `parameters` schema below, or it
        # overwrites the real parameters.type ("object") and the provider
        # rejects the request. See #30557.

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Use one of the four Anthropic tool_choice types: "auto", "any", "none", or "tool" (with a "name" field for "tool").
  2. If you meant OpenAI semantics, note "any" maps to OpenAI's "required"; for a specific tool use {"type": "tool", "name": "..."}.
  3. Check exact casing — the match is case-sensitive.
  4. If Anthropic shipped a new tool_choice type, upgrade litellm to a version that supports it.

Example fix

# before
tool_choice = {"type": "function", "function": {"name": "get_weather"}}  # ValueError

# after
tool_choice = {"type": "tool", "name": "get_weather"}
Defensive patterns

Strategy: type-guard

Validate before calling

VALID_TOOL_CHOICE_TYPES = {"auto", "any", "none", "tool"}

def validate_tool_choice(tool_choice: dict) -> None:
    if tool_choice["type"] not in VALID_TOOL_CHOICE_TYPES:
        raise ValueError(f"tool_choice.type must be one of {sorted(VALID_TOOL_CHOICE_TYPES)}")
    if tool_choice["type"] == "tool" and not tool_choice.get("name"):
        raise ValueError("tool_choice type 'tool' requires a 'name'")

Type guard

def is_valid_anthropic_tool_choice(tc: object) -> bool:
    return (
        isinstance(tc, dict)
        and tc.get("type") in {"auto", "any", "none", "tool"}
        and (tc["type"] != "tool" or isinstance(tc.get("name"), str) and bool(tc["name"]))
    )

Try / catch

try:
    openai_tc = adapter.translate_anthropic_tool_choice(tool_choice)
except ValueError as e:
    if "Incompatible tool choice" in str(e):
        return http_error(400, str(e))
    raise

Prevention

When it happens

Trigger: Sending a tool_choice dict whose 'type' is anything other than auto/any/none/tool, e.g. {"type": "function"} (OpenAI-style value sent to the Anthropic endpoint), {"type": "required"}, or a typo like {"type": "Tool"} (case-sensitive match).

Common situations: Copy-pasting OpenAI tool_choice syntax ({"type": "function", "function": {...}}) into an Anthropic-format request; version drift where a newer Anthropic tool_choice type is not yet supported by the installed litellm; case or spelling mistakes in the type string.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/8d004f25f015cfc1. Report an issue: GitHub.