Two errors for the strict end of tool calling. ToolChoiceViolationError: the response violated the toolChoice you enforced (e.g. you required a specific tool and the model called none). ToolCallRepairError: your repair function threw while trying to fix a malformed tool call.

import { generateText, tool } from 'ai';
import { z } from 'zod';

const result = await generateText({
  model: 'your-model-id',
  tools: { getWeather: tool({ inputSchema: z.object({ city: z.string() }), execute: async ({ city }) => ({ city }) }) },
  toolChoice: { type: 'tool', toolName: 'getWeather' },
  prompt: 'Hi there',
});

What to know:
1. toolChoice: 'required' or a specific tool forces the model's hand. When the model cannot comply (the prompt does not need tools), you get ToolChoiceViolationError instead of a graceful text answer. Only enforce when the task genuinely needs the tool.
2. repairToolCall receives the malformed call and returns a fixed one. If your repair function throws or returns garbage, ToolCallRepairError replaces the original error. Keep repair functions total: handle every shape, never throw.
3. Log the raw tool call inside repairToolCall during development. The most common malformations are wrong arg types (string where number expected) and extra unknown fields.
4. InvalidToolInputError is what repair is trying to prevent. The pipeline is: model call -> validation fails -> repairToolCall -> revalidate -> InvalidToolInputError or ToolCallRepairError if still broken.
5. For user-facing tools, prefer a schema the model can actually satisfy over aggressive repair. Simpler schemas beat clever repairs.
6. Test toolChoice enforcement with prompts that do and do not need tools. Enforcement that only works on the happy path will surprise you in production.