microsoft/autogen · error · ValueError
tool_choice specified but no tools provided
Error message
tool_choice specified but no tools provided
What it means
create_stream() requires that any tool_choice other than 'auto'/'none' be accompanied by a non-empty tools sequence. Passing tool_choice='required' or a Tool instance with len(tools)==0 raises this ValueError immediately. It mirrors the non-streaming contract: forcing tool use is meaningless with no tools to force.
Source
Thrown at python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/_llama_cpp_completion_client.py:423
async def create_stream(
self,
messages: Sequence[LLMMessage],
*,
tools: Sequence[Tool | ToolSchema] = [],
tool_choice: Tool | Literal["auto", "required", "none"] = "auto",
# None means do not override the default
# A value means to override the client default - often specified in the constructor
json_output: Optional[bool | type[BaseModel]] = None,
extra_create_args: Mapping[str, Any] = {},
cancellation_token: Optional[CancellationToken] = None,
) -> AsyncGenerator[Union[str, CreateResult], None]:
# Validate tool_choice parameter even though streaming is not implemented
if tool_choice != "auto" and tool_choice != "none":
if not self.model_info["function_calling"]:
raise ValueError("tool_choice specified but model does not support function calling")
if len(tools) == 0:
raise ValueError("tool_choice specified but no tools provided")
logger.warning("tool_choice parameter specified but may not be supported by llama-cpp-python")
raise NotImplementedError("Stream not yet implemented for LlamaCppChatCompletionClient")
yield ""
# Implement abstract methods
def actual_usage(self) -> RequestUsage:
return RequestUsage(
prompt_tokens=self._total_usage.get("prompt_tokens", 0),
completion_tokens=self._total_usage.get("completion_tokens", 0),
)
@property
def capabilities(self) -> ModelInfo:
return self.model_info
def count_tokens(
self,View on GitHub (pinned to 027ecf0a37)
Solutions
- Supply the tools: create_stream(messages, tools=[tool1], tool_choice='required')
- Or reset tool_choice='auto' on code paths where no tools are registered
- Guard at the call site: tool_choice = 'required' if tools else 'auto'
Example fix
# before async for c in client.create_stream(msgs, tool_choice="required"): ... # after tool_choice = "required" if tools else "auto" async for c in client.create_stream(msgs, tools=tools, tool_choice=tool_choice): ...
Defensive patterns
Strategy: validation
Validate before calling
if tool_choice not in ("auto", "none") and len(tools) == 0:
tool_choice = "auto" # or raise your own config error before the call Try / catch
try:
... # call with tool_choice
except ValueError as e:
if "no tools provided" in str(e):
result = await client.create(messages, tools=default_tools, tool_choice=tool_choice)
else:
raise Prevention
- Compute tool_choice from the tools list: 'required' if tools else 'auto'
- Register fallback tools once at startup so the list is never empty when forcing
- Validate (tool_choice, tools) pairs in request-builder helpers
When it happens
Trigger: create_stream(messages, tool_choice='required') with no tools argument (default empty list); tools=[] passed explicitly; a pipeline that conditionally clears tools but leaves tool_choice set.
Common situations: Template code that always sets tool_choice='required' but only sometimes attaches tools; refactoring that moved tool registration behind a flag without defaulting tool_choice back to 'auto'.
Related errors
- tool_choice specified but model does not support function ca
- Role mismatch
- Unsupported message type: {type(msg)}
- json_output must be a boolean, a BaseModel subclass or None.
- Unexpected response type from LlamaCpp model.
AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15).
Data as JSON: /api/errors/abc03d001f58b972.
Report an issue: GitHub.