{"record":{"id":"adb34753879b35f0","repo":"microsoft/autogen","slug":"tool-choice-must-be-a-tool-object-auto-requir","errorCode":null,"errorMessage":"tool_choice must be a Tool object, 'auto', 'required', or 'none', got {type(tool_choice)}","messagePattern":"tool_choice must be a Tool object, 'auto', 'required', or 'none', got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/models/anthropic/_anthropic_client.py","lineNumber":175,"sourceCode":"        tool_choice: A single Tool object to force the model to use, \"auto\" to let the model choose any available tool, \"required\" to force tool usage, or \"none\" to disable tool usage.\n\n    Returns:\n        Anthropic API compatible tool_choice value.\n    \"\"\"\n    if tool_choice == \"none\":\n        return {\"type\": \"none\"}\n\n    if tool_choice == \"auto\":\n        return {\"type\": \"auto\"}\n\n    if tool_choice == \"required\":\n        return {\"type\": \"any\"}  # Anthropic uses \"any\" for required\n\n    # Must be a Tool object\n    if isinstance(tool_choice, Tool):\n        return {\"type\": \"tool\", \"name\": tool_choice.schema[\"name\"]}\n    else:\n        raise ValueError(f\"tool_choice must be a Tool object, 'auto', 'required', or 'none', got {type(tool_choice)}\")\n\n\n@overload\ndef __empty_content_to_whitespace(content: str) -> str: ...\n\n\n@overload\ndef __empty_content_to_whitespace(content: List[Any]) -> Iterable[Any]: ...\n\n\ndef __empty_content_to_whitespace(\n    content: Union[str, List[Union[str, Image]]],\n) -> Union[str, Iterable[Any]]:\n    if isinstance(content, str) and not content.strip():\n        return \" \"\n    elif isinstance(content, list) and not any(isinstance(x, str) and not x.strip() for x in content):\n        for idx, message in enumerate(content):\n            if isinstance(message, str) and not message.strip():","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/models/anthropic/_anthropic_client.py#L157-L193","documentation":"AnthropicChatCompletionClient.create accepts tool_choice as either the strings 'auto', 'required', 'none' or a Tool object (to force a specific tool). The conversion helper maps these to Anthropic's format and raises ValueError with the actual received type for anything else — e.g. a raw dict {'type': 'tool', ...} or an OpenAI-style ToolChoiceOption.","triggerScenarios":"create(..., tool_choice={'type': 'function', 'function': {'name': 'x'}}) (OpenAI format); passing the tool's name string instead of the Tool object; passing None when you meant 'auto'; passing a pydantic schema instead of a Tool instance.","commonSituations":"Porting code from OpenAIChatCompletionClient where dict tool_choice is idiomatic; LLM-generated or config-driven tool_choice values arriving as dicts; assuming string tool names force tools.","solutions":["Pass a Tool object built by the same tools list you give create(): tool_choice=next(t for t in tools if t.schema['name'] == wanted).","Use the string literals 'auto' | 'required' | 'none' for the corresponding behaviors.","If tool_choice comes from config, validate/normalize it to Tool or one of the three strings before calling create."],"exampleFix":"# before\nresult = await client.create(messages, tools=tools, tool_choice={'type': 'tool', 'name': 'get_weather'})  # ValueError\n\n# after\nforced = next(t for t in tools if t.schema['name'] == 'get_weather')\nresult = await client.create(messages, tools=tools, tool_choice=forced)","handlingStrategy":"type-guard","validationCode":"from autogen_core.models import Tool\n\ndef coerce_tool_choice(choice, tools):\n    if choice in ('auto', 'required', 'none'):\n        return choice\n    if isinstance(choice, Tool):\n        return choice\n    if isinstance(choice, str):\n        return next(t for t in tools if t.schema['name'] == choice)\n    raise TypeError(f'bad tool_choice: {choice!r}')","typeGuard":"def is_valid_tool_choice(choice) -> bool:\n    return choice in ('auto', 'required', 'none') or isinstance(choice, Tool)","tryCatchPattern":"try:\n    res = await client.create(messages, tools=tools, tool_choice=tc)\nexcept ValueError as e:\n    if 'tool_choice' in str(e):\n        res = await client.create(messages, tools=tools)  # default 'auto'\n    else:\n        raise","preventionTips":["Always resolve forced tools to the Tool object from your tools list","Never port OpenAI-style dict tool_choice directly","Normalize config-driven tool_choice through one coercion helper"],"tags":["anthropic","tool-choice","llm-client","type-error"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}