{"record":{"id":"c67d17f499fec4aa","repo":"vercel/ai","slug":"callers-for-tool-toolname-must-be-an-array","errorCode":null,"errorMessage":"callers for tool \"${toolName}\" must be an array.","messagePattern":"callers for tool \"(.+?)\" must be an array\\.","errorType":"validation","errorClass":"InvalidArgumentError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/generate-text/tool-caller-configuration.ts","lineNumber":49,"sourceCode":"  toolCallers: Experimental_ToolCallers<TOOLS> | undefined;\n}): ResolvedToolCallers | undefined {\n  if (tools == null || toolCallers == null) {\n    return undefined;\n  }\n\n  const resolved: ResolvedToolCallers = {};\n\n  for (const [toolName, callers] of Object.entries(toolCallers)) {\n    if (!Object.prototype.hasOwnProperty.call(tools, toolName)) {\n      throw new InvalidArgumentError({\n        parameter: 'experimental_toolCallers',\n        value: toolCallers,\n        message: `unknown tool \"${toolName}\".`,\n      });\n    }\n\n    if (!Array.isArray(callers)) {\n      throw new InvalidArgumentError({\n        parameter: 'experimental_toolCallers',\n        value: toolCallers,\n        message: `callers for tool \"${toolName}\" must be an array.`,\n      });\n    }\n\n    resolved[toolName] = callers.map(caller => {\n      if (caller === DIRECT_TOOL_CALL) {\n        return caller;\n      }\n\n      if (\n        typeof caller !== 'string' ||\n        !Object.prototype.hasOwnProperty.call(tools, caller) ||\n        experimental_getToolCaller(tools[caller]) == null\n      ) {\n        throw new InvalidArgumentError({\n          parameter: 'experimental_toolCallers',","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/ai/src/generate-text/tool-caller-configuration.ts#L31-L67","documentation":"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.","triggerScenarios":"Passing `experimental_toolCallers: { myTool: 'otherTool' }` (bare string) or any non-array value instead of `myTool: ['otherTool']`.","commonSituations":"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.","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."],"exampleFix":"// before\nexperimental_toolCallers: { summarize: 'summarizerTool' }\n\n// after\nexperimental_toolCallers: { summarize: ['summarizerTool'] }","handlingStrategy":"validation","validationCode":"function normalizeToolCallers(toolCallers: Record<string, unknown>) {\n  for (const [name, callers] of Object.entries(toolCallers)) {\n    if (!Array.isArray(callers)) {\n      throw new Error(`callers for tool \"${name}\" must be an array`);\n    }\n  }\n}","typeGuard":"function isToolCallersConfig(\n  v: unknown\n): v is Record<string, string[]> {\n  return (\n    typeof v === 'object' && v !== null &&\n    Object.values(v).every(arr => Array.isArray(arr) && arr.every(c => typeof c === 'string'))\n  );\n}","tryCatchPattern":"try {\n  await streamText({ tools, experimental_toolCallers, ... });\n} catch (error) {\n  if (InvalidArgumentError.isInstance(error) && error.parameter === 'experimental_toolCallers') {\n    // normalize: wrap scalar values in arrays, then retry\n  } else throw error;\n}","preventionTips":["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."],"tags":["tools","configuration","invalid-argument","type-error"],"backgroundTag":"invalid-argument-type","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}