microsoft/autogen · error · ValueError

tool_choice specified but no tools provided

Error message

tool_choice specified but no tools provided

What it means

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.

Source

Thrown at python/packages/autogen-ext/src/autogen_ext/models/openai/_openai_client.py:636

                prepend_name=self._add_name_prefixes,
                model=create_args.get("model", "unknown"),
                model_family=self._model_info["family"],
                include_name_in_message=self._include_name_in_message,
            )
            for m in messages
        ]

        oai_messages = [item for sublist in oai_messages_nested for item in sublist]

        if self.model_info["function_calling"] is False and len(tools) > 0:
            raise ValueError("Model does not support function calling")

        converted_tools = convert_tools(tools)

        # Process tool_choice parameter
        if isinstance(tool_choice, Tool):
            if len(tools) == 0:
                raise ValueError("tool_choice specified but no tools provided")

            # Validate that the tool exists in the provided tools
            tool_names_available: List[str] = []
            for tool in tools:
                if isinstance(tool, Tool):
                    tool_names_available.append(tool.schema["name"])
                else:
                    tool_names_available.append(tool["name"])

            # tool_choice is a single Tool object
            tool_name = tool_choice.schema["name"]
            if tool_name not in tool_names_available:
                raise ValueError(f"tool_choice references '{tool_name}' but it's not in the provided tools")

        if len(converted_tools) > 0:
            # Convert to OpenAI format and add to create_args
            converted_tool_choice = convert_tool_choice(tool_choice)
            create_args["tool_choice"] = converted_tool_choice

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Pass the referenced tool in the tools list: tools=[tool_choice, ...]
  2. Skip setting tool_choice (or set it to 'none') when the tools list is empty
  3. Guard at the call site: only set a Tool tool_choice when len(tools) > 0

Example fix

# before
await client.create([msg], tools=[], tool_choice=required_tool)

# after
await client.create([msg], tools=[required_tool], tool_choice=required_tool)
Defensive patterns

Strategy: validation

Validate before calling

from autogen_core.tools import Tool

if isinstance(tool_choice, Tool):
    assert tools, "tool_choice Tool requires a non-empty tools list"
    assert tool_choice in tools or tool_choice.schema["name"] in {getattr(t, 'schema', t)['name'] for t in tools}

Type guard

def tool_choice_is_consistent(tool_choice, tools) -> bool:
    if not isinstance(tool_choice, Tool):
        return True
    names = {t.schema["name"] if isinstance(t, Tool) else t["name"] for t in tools}
    return len(tools) > 0 and tool_choice.schema["name"] in names

Try / catch

try:
    result = await client.create(messages, tools=tools, tool_choice=tool_choice)
except ValueError as e:
    if "tool_choice" in str(e):
        result = await client.create(messages, tools=tools, tool_choice="auto")
    else:
        raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/c34c00c43bf8d84a. Report an issue: GitHub.