{"record":{"id":"4e6a54342a09fdea","repo":"zylon-ai/private-gpt","slug":"tool-choice-self-tool-choice-is-not-in-the-pro","errorCode":null,"errorMessage":"Tool choice '{self.tool_choice}' is not in the provided tools.","messagePattern":"Tool choice '(.+?)' is not in the provided tools\\.","errorType":"validation","errorClass":"ValueError","httpStatus":422,"severity":"error","filePath":"private_gpt/server/chat/chat_models.py","lineNumber":333,"sourceCode":"        for message in self.messages:\n            if not message.content:\n                raise ValueError(f\"Message content cannot be empty: {message}\")\n            if isinstance(message.content, list):\n                for block in message.content:\n                    if block is None:\n                        raise ValueError(f\"Block cannot be None: {message}\")\n\n        if self.messages[-1].role not in self._valid_last_message_roles:\n            raise ValueError(\n                f\"Last message role must be one of {self._valid_last_message_roles}, but got {self.messages[-1].role}\"\n            )\n\n        # Check tools and tool choice\n        if self.tools and self.tool_choice and self.tool_choice.type == \"tool\":\n            if not self.tools:\n                raise ValueError(\"Tool choice is set, but no tools are provided.\")\n            if self.tool_choice.name not in [tool.name for tool in self.tools]:\n                raise ValueError(\n                    f\"Tool choice '{self.tool_choice}' is not in the provided tools.\"\n                )\n\n        if not self.tools and self.tool_context:\n            raise ValueError(\n                \"Tool context is provided, but no tools are specified. \"\n                \"Please provide tools to use with the tool context.\"\n            )\n\n        # Apply global tool context to tools without specific context\n        if self.tools is not None:\n            global_tool_context = self.tool_context or []\n            if global_tool_context:\n                for tool in self.tools:\n                    if tool.context is None:\n                        tool.context = global_tool_context\n\n        has_structured_output = bool(self.output_config and self.output_config.format)","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/server/chat/chat_models.py#L315-L351","documentation":"ChatBody rejects a request where tool_choice.type == 'tool' (forcing a specific function) but tool_choice.name does not match any name in the tools list. The API cannot force a function it was not given, so validation fails with the offending tool_choice printed in the message.","triggerScenarios":"tool_choice={\"type\":\"tool\",\"name\":\"lookup\"} while tools only defines e.g. 'search'; renaming a tool in the tools registry without updating the client's tool_choice; dynamic tool filtering that removes the forced tool.","commonSituations":"Version skew between client tool names and server-registered tools; feature-flagged tool sets where the forced tool is disabled; typos in the tool name string.","solutions":["Make tool_choice.name exactly match one of the tool names in the same request's tools array.","Derive tool_choice from the tools list programmatically instead of hardcoding the name.","If the tool was deliberately removed, switch tool_choice to {\"type\":\"auto\"} or another available tool."],"exampleFix":"# before\n{\"tools\":[{\"name\":\"search\",\"type\":\"...\"}],\"tool_choice\":{\"type\":\"tool\",\"name\":\"lookup\"}}\n\n# after\n{\"tools\":[{\"name\":\"search\",\"type\":\"...\"}],\"tool_choice\":{\"type\":\"tool\",\"name\":\"search\"}}","handlingStrategy":"validation","validationCode":"if tool_choice and tool_choice.get('type') == 'tool':\n    assert tool_choice['name'] in {t['name'] for t in tools}, 'forced tool must be in tools'","typeGuard":"def tool_choice_is_valid(tools: list[dict], tc: dict | None) -> bool:\n    return tc is None or tc.get('type') != 'tool' or tc.get('name') in {t['name'] for t in tools}","tryCatchPattern":"except ValidationError as e:\n    if 'not in the provided tools' in str(e):\n        tool_choice = {'type': 'auto'}; retry()\n    else:\n        raise","preventionTips":["Derive tool_choice.name from the same tools array you send","Namespace tool names to avoid cross-source mismatches","Add a unit test asserting tool_choice names against the registered tool set"],"tags":["validation","tools","tool-choice","chat-api"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}