vllm-project/vllm · error · Error
tool_choice requires at least one available tool
Error message
tool_choice requires at least one available tool
What it means
Request validation error (`request.rs:514`): the request sets a `tool_choice` (any mode other than 'none', or a specific function choice) but the `tools` array is empty or absent. Since there is nothing to choose from, the request is rejected as a 4xx user error (`error.rs:96`).
Source
Thrown at rust/src/chat/src/error.rs:76
selection: String,
},
#[error("harmony output parsing failed")]
HarmonyOutputParsing {
#[source]
error: BoxedError,
},
#[error(
"this model's maximum context length is {max_model_len} tokens, \
but the prompt contains {prompt_len} input tokens"
)]
PromptTooLong { max_model_len: u32, prompt_len: u32 },
#[error("chat request stream `{request_id}` closed before terminal output")]
StreamClosedBeforeTerminalOutput { request_id: String },
#[error("tool call stream state is inconsistent: {message}")]
ToolCallStreamInvariant { message: String },
#[error("duplicate tool name `{name}`")]
DuplicateToolName { name: String },
#[error("tool_choice requires at least one available tool")]
ToolChoiceRequiresTools,
#[error("tool_choice function `{name}` was not found in the available tools")]
ToolChoiceFunctionNotFound { name: String },
#[error("failed to build structural tag: {message}")]
StructuralTag { message: String },
#[error(transparent)]
Text(#[from] vllm_text::Error),
#[error(transparent)]
Tokenizer(#[from] vllm_tokenizer::TokenizerError),
}
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
/// Whether this error represents invalid user request parameters.
pub fn is_request_validation_error(&self) -> bool {
match self {
Self::PromptTooLong { .. }View on GitHub (pinned to c794754062)
Solutions
- Include at least one tool definition whenever `tool_choice` is not 'none'.
- If no tools are available, omit `tool_choice` entirely or set it to 'none'.
- Add a client-side check: `if tool_choice != none && tools.is_empty() { drop tool_choice }`.
Example fix
// before
req.tool_choice = Some(ToolChoice::Auto); // tools list empty
// after
if !tools.is_empty() {
req.tool_choice = Some(ToolChoice::Auto);
req.tools = tools;
} Defensive patterns
Strategy: validation
Validate before calling
if matches!(request.tool_choice, Some(ToolChoice::Mode(_)) | Some(ToolChoice::Function(_)))
&& request.tools.as_ref().is_none_or(Vec::is_empty)
{
return Err("tool_choice requires at least one tool");
} Type guard
fn tool_choice_is_valid(choice: Option<&ToolChoice>, tools: &[Tool]) -> bool {
match choice {
None | Some(ToolChoice::Mode(Mode::None)) => true,
Some(_) => !tools.is_empty(),
}
} Try / catch
match result {
Err(vllm_chat::Error::ToolChoiceRequiresTools) => {
respond_bad_request("set tools or change tool_choice to none")
}
other => other?,
} Prevention
- Bind tool_choice and tools together in one request-builder method so they cannot diverge.
- Default tool_choice to none when the tool list is empty.
When it happens
Trigger: `{"tool_choice": "auto", "messages": [...]}` without a `tools` field; or `tools: []` combined with `tool_choice: {type:"function", ...}`; client code that conditionally attaches tools but always sets tool_choice.
Common situations: Dynamic agent code attaching tools only when a plugin is loaded, while hardcoding `tool_choice: "auto"`; refactoring that moved the tools field and dropped it.
Related errors
- tool_choice function `{name}` was not found in the available
- this model's maximum context length is {max_model_len} token
- duplicate tool name `{name}`
- chat request stream `{request_id}` closed before terminal ou
- failed to build structural tag: {message}
AI-assisted analysis of vllm-project/vllm@c794754062 (2026-08-14).
Data as JSON: /api/errors/eec51416a8a25115.
Report an issue: GitHub.