{"record":{"id":"8d004f25f015cfc1","repo":"BerriAI/litellm","slug":"incompatible-tool-choice-param-submitted-tool-c","errorCode":null,"errorMessage":"Incompatible tool choice param submitted - {tool_choice}","messagePattern":"Incompatible tool choice param submitted - (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py","lineNumber":710,"sourceCode":"        return reasoning_effort\n\n    def translate_anthropic_tool_choice_to_openai(\n        self, tool_choice: AnthropicMessagesToolChoice\n    ) -> ChatCompletionToolChoiceValues:\n        if tool_choice[\"type\"] == \"any\":\n            return \"required\"\n        elif tool_choice[\"type\"] == \"auto\":\n            return \"auto\"\n        elif tool_choice[\"type\"] == \"tool\":\n            # Truncate tool name if it exceeds OpenAI's 64-char limit\n            original_name: Final = tool_choice.get(\"name\", \"\")\n            truncated_name: Final = truncate_tool_name(original_name)\n            tc_function_param: Final = ChatCompletionToolChoiceFunctionParam(name=truncated_name)\n            return ChatCompletionToolChoiceObjectParam(type=\"function\", function=tc_function_param)\n        elif tool_choice[\"type\"] == \"none\":\n            return \"none\"\n        else:\n            raise ValueError(f\"Incompatible tool choice param submitted - {tool_choice}\")\n\n    def translate_anthropic_tools_to_openai(\n        self, tools: list[AllAnthropicToolsValues], model: str | None = None\n    ) -> tuple[list[ChatCompletionToolParam], dict[str, str]]:\n        \"\"\"\n        Translate Anthropic tools to OpenAI format.\n\n        Returns:\n            Tuple of (translated_tools, tool_name_mapping)\n            - tool_name_mapping maps truncated names back to original names\n              for tools that exceeded OpenAI's 64-char limit\n        \"\"\"\n        new_tools: Final[list[ChatCompletionToolParam]] = []\n        tool_name_mapping: Final[dict[str, str]] = {}\n        # \"type\" is the Anthropic tool type (e.g. \"custom\"); it must not be\n        # merged into the OpenAI function `parameters` schema below, or it\n        # overwrites the real parameters.type (\"object\") and the provider\n        # rejects the request. See #30557.","sourceCodeStart":692,"sourceCodeEnd":728,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py#L692-L728","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Use one of the four Anthropic tool_choice types: \"auto\", \"any\", \"none\", or \"tool\" (with a \"name\" field for \"tool\").","If you meant OpenAI semantics, note \"any\" maps to OpenAI's \"required\"; for a specific tool use {\"type\": \"tool\", \"name\": \"...\"}.","Check exact casing — the match is case-sensitive.","If Anthropic shipped a new tool_choice type, upgrade litellm to a version that supports it."],"exampleFix":"# before\ntool_choice = {\"type\": \"function\", \"function\": {\"name\": \"get_weather\"}}  # ValueError\n\n# after\ntool_choice = {\"type\": \"tool\", \"name\": \"get_weather\"}","handlingStrategy":"type-guard","validationCode":"VALID_TOOL_CHOICE_TYPES = {\"auto\", \"any\", \"none\", \"tool\"}\n\ndef validate_tool_choice(tool_choice: dict) -> None:\n    if tool_choice[\"type\"] not in VALID_TOOL_CHOICE_TYPES:\n        raise ValueError(f\"tool_choice.type must be one of {sorted(VALID_TOOL_CHOICE_TYPES)}\")\n    if tool_choice[\"type\"] == \"tool\" and not tool_choice.get(\"name\"):\n        raise ValueError(\"tool_choice type 'tool' requires a 'name'\")","typeGuard":"def is_valid_anthropic_tool_choice(tc: object) -> bool:\n    return (\n        isinstance(tc, dict)\n        and tc.get(\"type\") in {\"auto\", \"any\", \"none\", \"tool\"}\n        and (tc[\"type\"] != \"tool\" or isinstance(tc.get(\"name\"), str) and bool(tc[\"name\"]))\n    )","tryCatchPattern":"try:\n    openai_tc = adapter.translate_anthropic_tool_choice(tool_choice)\nexcept ValueError as e:\n    if \"Incompatible tool choice\" in str(e):\n        return http_error(400, str(e))\n    raise","preventionTips":["Never reuse OpenAI tool_choice JSON ({\"type\": \"function\", ...}) on Anthropic-format endpoints.","Centralize tool_choice construction in one helper that only emits the four valid types.","Check the installed litellm version when Anthropic adds new tool_choice types."],"tags":["anthropic","tool-choice","translation","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}