zylon-ai/private-gpt · error · ValueError
Tool choice '{self.tool_choice}' is not in the provided tool
Error message
Tool choice '{self.tool_choice}' is not in the provided tools. What it means
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.
Source
Thrown at private_gpt/server/chat/chat_models.py:333
for message in self.messages:
if not message.content:
raise ValueError(f"Message content cannot be empty: {message}")
if isinstance(message.content, list):
for block in message.content:
if block is None:
raise ValueError(f"Block cannot be None: {message}")
if self.messages[-1].role not in self._valid_last_message_roles:
raise ValueError(
f"Last message role must be one of {self._valid_last_message_roles}, but got {self.messages[-1].role}"
)
# Check tools and tool choice
if self.tools and self.tool_choice and self.tool_choice.type == "tool":
if not self.tools:
raise ValueError("Tool choice is set, but no tools are provided.")
if self.tool_choice.name not in [tool.name for tool in self.tools]:
raise ValueError(
f"Tool choice '{self.tool_choice}' is not in the provided tools."
)
if not self.tools and self.tool_context:
raise ValueError(
"Tool context is provided, but no tools are specified. "
"Please provide tools to use with the tool context."
)
# Apply global tool context to tools without specific context
if self.tools is not None:
global_tool_context = self.tool_context or []
if global_tool_context:
for tool in self.tools:
if tool.context is None:
tool.context = global_tool_context
has_structured_output = bool(self.output_config and self.output_config.format)View on GitHub (pinned to 4a030776a3)
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.
Example fix
# before
{"tools":[{"name":"search","type":"..."}],"tool_choice":{"type":"tool","name":"lookup"}}
# after
{"tools":[{"name":"search","type":"..."}],"tool_choice":{"type":"tool","name":"search"}} Defensive patterns
Strategy: validation
Validate before calling
if tool_choice and tool_choice.get('type') == 'tool':
assert tool_choice['name'] in {t['name'] for t in tools}, 'forced tool must be in tools' Type guard
def tool_choice_is_valid(tools: list[dict], tc: dict | None) -> bool:
return tc is None or tc.get('type') != 'tool' or tc.get('name') in {t['name'] for t in tools} Try / catch
except ValidationError as e:
if 'not in the provided tools' in str(e):
tool_choice = {'type': 'auto'}; retry()
else:
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Tool context is provided, but no tools are specified. Please
- Tools are not supported when response_format is set to json_
- Duplicate tool names found in the tools list. Provided tools
- TOOL_NAME_CONFLICT
- mistral-common only supports function tools.
AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15).
Data as JSON: /api/errors/4e6a54342a09fdea.
Report an issue: GitHub.