vercel/ai · error · UnsupportedFunctionalityError
tool choice type: ${_exhaustiveCheck}
Error message
tool choice type: ${_exhaustiveCheck} What it means
The toolChoice mapping switch in prepareResponsesTools is exhaustive over the known tool-choice types. When an unknown/unmapped tool choice type reaches the default branch, TypeScript's never check captures it and the SDK throws UnsupportedFunctionalityError naming the unrecognized type, guarding against silently sending invalid tool_choice values to OpenAI.
Source
Thrown at packages/openai/src/responses/openai-responses-prepare-tools.ts:541
resolvedToolName === 'code_interpreter' ||
resolvedToolName === 'file_search' ||
resolvedToolName === 'image_generation' ||
resolvedToolName === 'web_search_preview' ||
resolvedToolName === 'web_search' ||
resolvedToolName === 'mcp' ||
resolvedToolName === 'apply_patch' ||
resolvedToolName === 'computer' ||
resolvedToolName === 'programmatic_tool_calling'
? { type: resolvedToolName }
: resolvedCustomProviderToolNames.has(resolvedToolName)
? { type: 'custom', name: resolvedToolName }
: { type: 'function', name: resolvedToolName },
toolWarnings,
};
}
default: {
const _exhaustiveCheck: never = type;
throw new UnsupportedFunctionalityError({
functionality: `tool choice type: ${_exhaustiveCheck}`,
});
}
}
}
function allowedToolKey(entry: OpenAIResponsesAllowedTool): string {
switch (entry.type) {
case 'mcp':
return `mcp:${entry.server_label}`;
case 'function':
case 'custom':
return `${entry.type}:${entry.name}`;
default:
return entry.type;
}
}
View on GitHub (pinned to 69428b1f8b)
Solutions
- Use one of the supported types: auto, none, required, or tool
- Validate/normalize toolChoice before passing it to the model
- Upgrade the AI SDK if you are using a newer tool-choice type added by OpenAI
- Cast only if you are certain the value is valid for the current API version
Example fix
// before
toolChoice: { type: 'any', toolName: 'getWeather' }
// after
toolChoice: { type: 'tool', toolName: 'getWeather' } Defensive patterns
Strategy: validation
Validate before calling
const VALID = new Set(['auto', 'none', 'required', 'tool']);
if (toolChoice && !VALID.has(toolChoice.type)) {
throw new Error(`Invalid toolChoice type: ${toolChoice.type}`);
} Type guard
function isValidToolChoice(tc: { type: string }): tc is { type: 'auto' | 'none' | 'required' } | { type: 'tool'; toolName: string } {
return tc.type === 'auto' || tc.type === 'none' || tc.type === 'required' ||
(tc.type === 'tool' && 'toolName' in tc);
} Try / catch
try {
await generateText({ model: openai.responses('gpt-4.1'), toolChoice, ... });
} catch (e) {
if (UnsupportedFunctionalityError.isInstance(e) && e.message.includes('tool choice type')) {
// fall back to toolChoice: 'auto' or surface a config error
} else throw e;
} Prevention
- Rely on the SDK's TypeScript types for toolChoice instead of hand-built objects
- Never construct toolChoice from unvalidated user/config input without a whitelist check
- Keep the SDK up to date when OpenAI adds new tool-choice types
When it happens
Trigger: Passing a toolChoice object whose 'type' is not one of 'auto' | 'none' | 'required' | 'tool' to an openai.responses(...) model, e.g. a mistyped type string or a tool-choice variant from a different provider/spec version.
Common situations: Typo like { type: 'auto ' } or { type: 'any' }; constructing toolChoice dynamically from user/config input without validation; provider spec drift where a newer type has not yet been mapped in the installed SDK version.
Related errors
- allowedTools with only tools that cannot be allow-listed (${
- AI_UnsupportedFunctionalityError
- AI_UnsupportedFunctionalityError
- 'tool choice type: ${_exhaustiveCheck}' functionality not su
- conflicting descriptions for OpenAI tool namespace "${namesp
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/e9f8d7fcab80596f.
Report an issue: GitHub.