vercel/ai · error · InvalidArgumentError

tool "${toolName}" contains an invalid caller.

Error message

tool "${toolName}" contains an invalid caller.

What it means

Every entry in a toolCallers array must itself be the name of an existing tool that exposes a caller (experimental_getToolCaller must return a value). resolveToolCallerConfiguration throws this InvalidArgumentError when a caller string is not a string, does not name a defined tool, or names a tool without a configured caller.

Source

Thrown at packages/ai/src/generate-text/tool-caller-configuration.ts:66

    if (!Array.isArray(callers)) {
      throw new InvalidArgumentError({
        parameter: 'experimental_toolCallers',
        value: toolCallers,
        message: `callers for tool "${toolName}" must be an array.`,
      });
    }

    resolved[toolName] = callers.map(caller => {
      if (caller === DIRECT_TOOL_CALL) {
        return caller;
      }

      if (
        typeof caller !== 'string' ||
        !Object.prototype.hasOwnProperty.call(tools, caller) ||
        experimental_getToolCaller(tools[caller]) == null
      ) {
        throw new InvalidArgumentError({
          parameter: 'experimental_toolCallers',
          value: toolCallers,
          message: `tool "${toolName}" contains an invalid caller.`,
        });
      }

      return caller;
    });
  }

  return resolved;
}

export function prepareToolsForToolCallers({
  tools,
  toolCallers,
}: {
  tools: ToolSet | undefined;

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Ensure each caller string names a tool present in `tools` that defines a tool caller implementation.
  2. Fix typos in the caller name.
  3. Add the caller tool to the tools map of the same call.
  4. Only mark caller-capable tools (e.g. agent tools with experimental tool caller support) as callers.

Example fix

// before
streamText({
  tools: { summarize },
  experimental_toolCallers: { summarize: ['summarizerAgent'] }, // summarizerAgent not in tools
});

// after
streamText({
  tools: { summarize, summarizerAgent },
  experimental_toolCallers: { summarize: ['summarizerAgent'] },
});
Defensive patterns

Strategy: validation

Validate before calling

function validateCallerEntries(tools: Record<string, unknown>, toolCallers: Record<string, string[]>) {
  for (const [name, callers] of Object.entries(toolCallers)) {
    for (const caller of callers) {
      if (typeof caller !== 'string' || !(caller in tools)) {
        throw new Error(`tool "${name}" references invalid caller "${String(caller)}"`);
      }
    }
  }
}

Type guard

function isKnownCaller<K extends string>(tools: Record<K, unknown>, caller: unknown): caller is K {
  return typeof caller === 'string' && caller in tools;
}

Try / catch

try {
  await generateText({ tools, experimental_toolCallers, ... });
} catch (error) {
  if (InvalidArgumentError.isInstance(error) && /invalid caller/.test(error.message)) {
    console.error('Caller must be an existing caller-capable tool:', error.message);
  } else throw error;
}

Prevention

When it happens

Trigger: Passing `experimental_toolCallers: { myTool: ['notATool'] }` where 'notATool' is missing from tools, is a non-string value, or is a tool that has no getToolCaller implementation (not itself callable as a caller).

Common situations: Listing an agent/model tool that wasn't included in the tools map; listing a plain function tool that cannot act as a caller; typos in the caller name; circular or self references that are not caller-capable tools.

Related errors


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