{"record":{"id":"abc03d001f58b972","repo":"microsoft/autogen","slug":"tool-choice-specified-but-no-tools-provided-abc03d","errorCode":null,"errorMessage":"tool_choice specified but no tools provided","messagePattern":"tool_choice specified but no tools provided","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/_llama_cpp_completion_client.py","lineNumber":423,"sourceCode":"\n    async def create_stream(\n        self,\n        messages: Sequence[LLMMessage],\n        *,\n        tools: Sequence[Tool | ToolSchema] = [],\n        tool_choice: Tool | Literal[\"auto\", \"required\", \"none\"] = \"auto\",\n        # None means do not override the default\n        # A value means to override the client default - often specified in the constructor\n        json_output: Optional[bool | type[BaseModel]] = None,\n        extra_create_args: Mapping[str, Any] = {},\n        cancellation_token: Optional[CancellationToken] = None,\n    ) -> AsyncGenerator[Union[str, CreateResult], None]:\n        # Validate tool_choice parameter even though streaming is not implemented\n        if tool_choice != \"auto\" and tool_choice != \"none\":\n            if not self.model_info[\"function_calling\"]:\n                raise ValueError(\"tool_choice specified but model does not support function calling\")\n            if len(tools) == 0:\n                raise ValueError(\"tool_choice specified but no tools provided\")\n            logger.warning(\"tool_choice parameter specified but may not be supported by llama-cpp-python\")\n\n        raise NotImplementedError(\"Stream not yet implemented for LlamaCppChatCompletionClient\")\n        yield \"\"\n\n    # Implement abstract methods\n    def actual_usage(self) -> RequestUsage:\n        return RequestUsage(\n            prompt_tokens=self._total_usage.get(\"prompt_tokens\", 0),\n            completion_tokens=self._total_usage.get(\"completion_tokens\", 0),\n        )\n\n    @property\n    def capabilities(self) -> ModelInfo:\n        return self.model_info\n\n    def count_tokens(\n        self,","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/models/llama_cpp/_llama_cpp_completion_client.py#L405-L441","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'.","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'"],"exampleFix":"# before\nasync for c in client.create_stream(msgs, tool_choice=\"required\"): ...\n\n# after\ntool_choice = \"required\" if tools else \"auto\"\nasync for c in client.create_stream(msgs, tools=tools, tool_choice=tool_choice): ...","handlingStrategy":"validation","validationCode":"if tool_choice not in (\"auto\", \"none\") and len(tools) == 0:\n    tool_choice = \"auto\"  # or raise your own config error before the call","typeGuard":null,"tryCatchPattern":"try:\n    ...  # call with tool_choice\nexcept ValueError as e:\n    if \"no tools provided\" in str(e):\n        result = await client.create(messages, tools=default_tools, tool_choice=tool_choice)\n    else:\n        raise","preventionTips":["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"],"tags":["llama-cpp","tool-calling","streaming","validation"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}