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

  1. Use one of the supported types: auto, none, required, or tool
  2. Validate/normalize toolChoice before passing it to the model
  3. Upgrade the AI SDK if you are using a newer tool-choice type added by OpenAI
  4. 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

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


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/e9f8d7fcab80596f. Report an issue: GitHub.