PrefectHQ/fastmcp · error · ValueError
Unsupported tool_choice mode: {tool_choice.mode!r}
Error message
Unsupported tool_choice mode: {tool_choice.mode!r} What it means
_convert_tool_choice_to_openai maps MCP ToolChoice modes 'auto', 'required', and 'none' to OpenAI tool_choice values. Any other mode string has no OpenAI equivalent and raises this ValueError with the offending mode repr.
Source
Thrown at fastmcp_slim/fastmcp/client/sampling/handlers/openai.py:449
parameters=parameters,
),
)
)
return openai_tools
@staticmethod
def _convert_tool_choice_to_openai(
tool_choice: ToolChoice,
) -> ChatCompletionToolChoiceOptionParam:
"""Convert MCP tool_choice to OpenAI format."""
if tool_choice.mode == "auto":
return "auto"
elif tool_choice.mode == "required":
return "required"
elif tool_choice.mode == "none":
return "none"
else:
raise ValueError(f"Unsupported tool_choice mode: {tool_choice.mode!r}")
@staticmethod
def _chat_completion_to_result_with_tools(
chat_completion: ChatCompletion,
) -> CreateMessageResultWithTools:
"""Convert OpenAI response to CreateMessageResultWithTools."""
if len(chat_completion.choices) == 0:
raise ValueError("No response for completion")
first_choice = chat_completion.choices[0]
message = first_choice.message
# Determine stop reason
stop_reason: StopReason
if first_choice.finish_reason == "tool_calls":
stop_reason = "toolUse"
elif first_choice.finish_reason == "stop":
stop_reason = "endTurn"View on GitHub (pinned to 1f02114297)
Solutions
- Set tool_choice.mode to 'auto', 'required', or 'none' before invoking the OpenAI handler.
- If you need 'force a specific function', pass the target tool name via sampling params the OpenAI path supports, or map it to 'required' plus prompt guidance.
- Translate Anthropic-style modes ('any' -> 'required') in an adapter before calling.
- Catch ValueError and default to 'auto' when strict forcing is not required.
Example fix
// before ToolChoice(mode='any') # anthropic-style // after ToolChoice(mode='required')
Defensive patterns
Strategy: validation
Validate before calling
VALID_MODES = {'auto', 'required', 'none'}
def assert_valid_tool_choice(tool_choice):
if tool_choice is not None and tool_choice.mode not in VALID_MODES:
raise ValueError(f'mode {tool_choice.mode!r} unsupported by OpenAI handler; use auto/required/none') Type guard
def is_openai_compatible_tool_choice(tc) -> bool:
return tc is None or getattr(tc, 'mode', None) in {'auto', 'required', 'none'} Try / catch
try:
result = await openai_handler(messages, params)
except ValueError as e:
if str(e).startswith('Unsupported tool_choice mode'):
params.toolChoice = ToolChoice(mode='auto')
result = await openai_handler(messages, params)
else:
raise Prevention
- Restrict tool_choice modes to auto/required/none for OpenAI
- Map provider-specific modes ('any') in an adapter layer
- Validate ToolChoice at config-load time, not at request time
- Add unit tests for every tool_choice mode you use
When it happens
Trigger: Calling the OpenAI sampling handler with a ToolChoice whose mode is not one of 'auto'/'required'/'none' — e.g. a provider-specific mode like 'any' or 'forced_function' carried over from another backend.
Common situations: Reusing tool_choice objects built for Anthropic-style handlers ('any' mode) with the OpenAI handler; config files with typo'd mode values; newer MCP SDK adding a mode enum member the handler hasn't mapped yet.
Related errors
- ImageContent is only supported in user messages for OpenAI
- AudioContent is only supported in user messages for OpenAI
- Unsupported tool_choice mode: {tool_choice.mode!r}
- Unsupported image MIME type for OpenAI: {content.mime_type!r
- Unsupported audio MIME type for OpenAI: {content.mime_type!r
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/edbb31c8c5b25423.
Report an issue: GitHub.