vercel/ai · error · NoSuchToolError

Model tried to call unavailable tool '${toolName}'. No tools

Error message

Model tried to call unavailable tool '${toolName}'. No tools are available.

What it means

NoSuchToolError thrown when the model emitted a tool call for a tool name that is not in the `tools` map passed to generateText/generateObject, and no tools at all were provided. The SDK cannot resolve an input schema or execute the call, so parsing the tool call fails. It guards against hallucinated tool names.

Source

Thrown at packages/ai/src/generate-text/parse-tool-call.ts:44

}: {
  toolCall: LanguageModelV4ToolCall;
  tools: TOOLS | undefined;
  repairToolCall: ToolCallRepairFunction<TOOLS> | undefined;
  refineToolInput?: ToolInputRefinement<TOOLS> | undefined;
  instructions: Instructions | undefined;
  messages: ModelMessage[];
}): Promise<TypedToolCall<TOOLS>> {
  try {
    if (tools == null) {
      // provider-executed dynamic tools are not part of our list of tools:
      if (toolCall.providerExecuted && toolCall.dynamic) {
        return await refineParsedToolCallInput({
          toolCall: await parseProviderExecutedDynamicToolCall(toolCall),
          refineToolInput,
        });
      }

      throw new NoSuchToolError({ toolName: toolCall.toolName });
    }

    try {
      return await refineParsedToolCallInput({
        toolCall: await doParseToolCall({ toolCall, tools }),
        refineToolInput,
      });
    } catch (error) {
      if (
        repairToolCall == null ||
        !(
          NoSuchToolError.isInstance(error) ||
          InvalidToolInputError.isInstance(error)
        )
      ) {
        throw error;
      }

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Pass the `tools` object to the generateText/streamText call that receives this response.
  2. If the model should not call tools, remove tool-calling instructions from the system prompt or use a model/endpoint without tool definitions.
  3. Use `experimental_repairToolCall` or catch NoSuchToolError (NoSuchToolError.isInstance) and skip/reprompt the model.

Example fix

// before
const { text } = await generateText({ model, prompt });
// after
const { text } = await generateText({ model, prompt, tools: { weather } });
Defensive patterns

Strategy: validation

Validate before calling

if (!tools || Object.keys(tools).length === 0) {
  throw new Error('generateText requires a non-empty tools map for this prompt');
}

Type guard

function hasTools<T extends Record<string, unknown>>(t?: T): t is T {
  return t != null && Object.keys(t).length > 0;
}

Try / catch

try {
  await generateText({ model, prompt, tools });
} catch (e) {
  if (NoSuchToolError.isInstance(e)) {
    // re-prompt with the list of available tools
  } else throw e;
}

Prevention

When it happens

Trigger: Calling generateText/streamText with no `tools` option while the model (trained or prompted to use tools) returns a tool_call; or the tools map is empty `{}` at the parse site.

Common situations: Forgetting to pass tools on one step/agent path while the system prompt still mentions them; using a different model endpoint that injects tool definitions; refactoring so tools are conditionally omitted; model hallucinating tools when tools: undefined.

Related errors


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