vercel/ai · error · UnsupportedFunctionalityError

'tool choice type: ${type}' functionality not supported.

Error message

'tool choice type: ${type}' functionality not supported.

What it means

prepareTools in the xAI provider validates the toolChoice argument against the exhaustive list of LanguageModelV4 tool choice types ('auto', 'none', 'required', 'tool'). The switch statement ends with a TypeScript exhaustiveness check (`const _exhaustiveCheck: never = type`), and throwing UnsupportedFunctionalityError means an unrecognized or unsupported tool choice type reached the provider. In practice this fires when a tool choice value outside the supported set is passed to generateText/streamText with an xAI model.

Source

Thrown at packages/xai/src/xai-prepare-tools.ts:94

    case 'auto':
    case 'none':
      return { tools: xaiTools, toolChoice: type, toolWarnings };
    case 'required':
      // xai supports 'required' directly
      return { tools: xaiTools, toolChoice: 'required', toolWarnings };
    case 'tool':
      // xai supports specific tool selection
      return {
        tools: xaiTools,
        toolChoice: {
          type: 'function',
          function: { name: toolChoice.toolName },
        },
        toolWarnings,
      };
    default: {
      const _exhaustiveCheck: never = type;
      throw new UnsupportedFunctionalityError({
        functionality: `tool choice type: ${_exhaustiveCheck}`,
      });
    }
  }
}

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Set toolChoice to one of the supported values: 'auto', 'none', 'required', or { type: 'tool', toolName: 'yourTool' }.
  2. Remove the toolChoice argument entirely (defaults to auto behavior) if the desired mode is not supported.
  3. Check for typos and that the value comes from the `ai` package types (ToolChoice), not another provider SDK.
  4. If using a custom provider wrapper, verify the toolChoice passes through the correct LanguageModelV4 typing instead of an untyped string.

Example fix

// before
const { text } = await generateText({ model: xai('grok-3'), tools, toolChoice: 'any' });

// after
const { text } = await generateText({ model: xai('grok-3'), tools, toolChoice: 'required' });
Defensive patterns

Strategy: validation

Validate before calling

const VALID = new Set(['auto', 'none', 'required']);
function isValidToolChoice(tc: unknown): boolean {
  return (
    typeof tc === 'string' && VALID.has(tc) ||
    (typeof tc === 'object' && tc !== null && (tc as any).type === 'tool' && typeof (tc as any).toolName === 'string')
  );
}
// assert before the call: if (toolChoice !== undefined && !isValidToolChoice(toolChoice)) throw ...

Type guard

function isToolChoice(v: unknown): v is 'auto' | 'none' | 'required' | { type: 'tool'; toolName: string } {
  return v === 'auto' || v === 'none' || v === 'required' ||
    (typeof v === 'object' && v !== null && (v as any).type === 'tool' && typeof (v as any).toolName === 'string');
}

Try / catch

try {
  await generateText({ model: xai('grok-3'), tools, toolChoice });
} catch (e) {
  if (UnsupportedFunctionalityError.isInstance(e)) {
    console.error('Unsupported toolChoice for xAI:', e.functionality);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling generateText/streamText with an xAI model and toolChoice set to a value that is not 'auto' | 'none' | 'required' | { type: 'tool', toolName } — e.g. a malformed object, an old provider-spec tool choice shape, or a raw string like 'any' from code written for another provider SDK.

Common situations: Porting code from OpenAI SDKs that use tool choice values like 'any' or 'function'; upgrading AI SDK versions and passing a stale toolChoice shape; hand-constructing toolChoice objects instead of using the union type; typos such as toolChoice: 'automatic'.

Related errors


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