vercel/ai · error · InvalidArgumentError
callers for tool "${toolName}" must be an array.
Error message
callers for tool "${toolName}" must be an array. What it means
Each value in `experimental_toolCallers` must be an array of caller tool names. resolveToolCallerConfiguration throws this InvalidArgumentError when a tool's callers value is not an array (e.g. a single string or an object was passed). The validation runs before any model call, so the request never reaches the provider.
Source
Thrown at packages/ai/src/generate-text/tool-caller-configuration.ts:49
toolCallers: Experimental_ToolCallers<TOOLS> | undefined;
}): ResolvedToolCallers | undefined {
if (tools == null || toolCallers == null) {
return undefined;
}
const resolved: ResolvedToolCallers = {};
for (const [toolName, callers] of Object.entries(toolCallers)) {
if (!Object.prototype.hasOwnProperty.call(tools, toolName)) {
throw new InvalidArgumentError({
parameter: 'experimental_toolCallers',
value: toolCallers,
message: `unknown tool "${toolName}".`,
});
}
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',View on GitHub (pinned to 69428b1f8b)
Solutions
- Wrap the caller name in an array: { toolName: ['callerTool'] }.
- If the value comes from config, normalize it (Array.isArray(x) ? x : [x]) before passing.
- Validate the toolCallers shape with a schema (e.g. zod) at startup.
Example fix
// before
experimental_toolCallers: { summarize: 'summarizerTool' }
// after
experimental_toolCallers: { summarize: ['summarizerTool'] } Defensive patterns
Strategy: validation
Validate before calling
function normalizeToolCallers(toolCallers: Record<string, unknown>) {
for (const [name, callers] of Object.entries(toolCallers)) {
if (!Array.isArray(callers)) {
throw new Error(`callers for tool "${name}" must be an array`);
}
}
} Type guard
function isToolCallersConfig(
v: unknown
): v is Record<string, string[]> {
return (
typeof v === 'object' && v !== null &&
Object.values(v).every(arr => Array.isArray(arr) && arr.every(c => typeof c === 'string'))
);
} Try / catch
try {
await streamText({ tools, experimental_toolCallers, ... });
} catch (error) {
if (InvalidArgumentError.isInstance(error) && error.parameter === 'experimental_toolCallers') {
// normalize: wrap scalar values in arrays, then retry
} else throw error;
} Prevention
- Type the toolCallers config as Record<string, string[]> end-to-end.
- Normalize external/JSON config (wrap scalars in arrays) before passing.
- Validate config with a zod schema at startup.
When it happens
Trigger: Passing `experimental_toolCallers: { myTool: 'otherTool' }` (bare string) or any non-array value instead of `myTool: ['otherTool']`.
Common situations: Misreading the API and passing a single caller as a string instead of a one-element array; config loaded from JSON/DB where the array wrapper was lost; merging configs where one entry was overwritten with a scalar.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- unknown tool "${toolName}".
- tool "${toolName}" contains an invalid caller.
- Invalid argument for parameter output: Invalid output type.
- Invalid argument for parameter enumValues: Enum values must
- Chunking must be "word", "line", a RegExp, an Intl.Segmenter
AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30).
Data as JSON: /api/errors/c67d17f499fec4aa.
Report an issue: GitHub.