{"record":{"id":"c34c00c43bf8d84a","repo":"microsoft/autogen","slug":"tool-choice-specified-but-no-tools-provided-c34c00","errorCode":null,"errorMessage":"tool_choice specified but no tools provided","messagePattern":"tool_choice specified but no tools provided","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/models/openai/_openai_client.py","lineNumber":636,"sourceCode":"                prepend_name=self._add_name_prefixes,\n                model=create_args.get(\"model\", \"unknown\"),\n                model_family=self._model_info[\"family\"],\n                include_name_in_message=self._include_name_in_message,\n            )\n            for m in messages\n        ]\n\n        oai_messages = [item for sublist in oai_messages_nested for item in sublist]\n\n        if self.model_info[\"function_calling\"] is False and len(tools) > 0:\n            raise ValueError(\"Model does not support function calling\")\n\n        converted_tools = convert_tools(tools)\n\n        # Process tool_choice parameter\n        if isinstance(tool_choice, Tool):\n            if len(tools) == 0:\n                raise ValueError(\"tool_choice specified but no tools provided\")\n\n            # Validate that the tool exists in the provided tools\n            tool_names_available: List[str] = []\n            for tool in tools:\n                if isinstance(tool, Tool):\n                    tool_names_available.append(tool.schema[\"name\"])\n                else:\n                    tool_names_available.append(tool[\"name\"])\n\n            # tool_choice is a single Tool object\n            tool_name = tool_choice.schema[\"name\"]\n            if tool_name not in tool_names_available:\n                raise ValueError(f\"tool_choice references '{tool_name}' but it's not in the provided tools\")\n\n        if len(converted_tools) > 0:\n            # Convert to OpenAI format and add to create_args\n            converted_tool_choice = convert_tool_choice(tool_choice)\n            create_args[\"tool_choice\"] = converted_tool_choice","sourceCodeStart":618,"sourceCodeEnd":654,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/models/openai/_openai_client.py#L618-L654","documentation":"Thrown by OpenAIChatCompletionClient when the tool_choice parameter is a Tool instance but the tools list is empty. A specific tool_choice forces the model to call a named tool, which is meaningless if no tools are provided, so the client rejects the combination immediately.","triggerScenarios":"Calling create with tool_choice=some_tool (a Tool object, not the string 'auto'/'required'/'none') while tools=[] or tools is omitted. String literals like 'auto' do not trigger this; only a Tool instance with an empty tools list does.","commonSituations":"Dynamically building a tools list that ends up empty (a filter removed everything) while a hardcoded tool_choice Tool is still passed; refactoring code that previously passed a non-empty tool set; agent frameworks forwarding a stale tool_choice.","solutions":["Pass the referenced tool in the tools list: tools=[tool_choice, ...]","Skip setting tool_choice (or set it to 'none') when the tools list is empty","Guard at the call site: only set a Tool tool_choice when len(tools) > 0"],"exampleFix":"# before\nawait client.create([msg], tools=[], tool_choice=required_tool)\n\n# after\nawait client.create([msg], tools=[required_tool], tool_choice=required_tool)","handlingStrategy":"validation","validationCode":"from autogen_core.tools import Tool\n\nif isinstance(tool_choice, Tool):\n    assert tools, \"tool_choice Tool requires a non-empty tools list\"\n    assert tool_choice in tools or tool_choice.schema[\"name\"] in {getattr(t, 'schema', t)['name'] for t in tools}","typeGuard":"def tool_choice_is_consistent(tool_choice, tools) -> bool:\n    if not isinstance(tool_choice, Tool):\n        return True\n    names = {t.schema[\"name\"] if isinstance(t, Tool) else t[\"name\"] for t in tools}\n    return len(tools) > 0 and tool_choice.schema[\"name\"] in names","tryCatchPattern":"try:\n    result = await client.create(messages, tools=tools, tool_choice=tool_choice)\nexcept ValueError as e:\n    if \"tool_choice\" in str(e):\n        result = await client.create(messages, tools=tools, tool_choice=\"auto\")\n    else:\n        raise","preventionTips":["Derive tool_choice from the same tools list you pass — never two independent sources","Default tool_choice to 'auto' and only force a Tool when the list is provably non-empty","Log the tools/tool_choice pair before each call during development"],"tags":["openai","tool-choice","tools","validation"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}