{"record":{"id":"0ae9ce9130e24e93","repo":"vercel/ai","slug":"unknown-tool-toolname","errorCode":null,"errorMessage":"unknown tool \"${toolName}\".","messagePattern":"unknown tool \"(.+?)\"\\.","errorType":"validation","errorClass":"InvalidArgumentError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/generate-text/tool-caller-configuration.ts","lineNumber":41,"sourceCode":"\nexport type ResolvedToolCallers = Record<string, ReadonlyArray<string>>;\n\nexport function resolveToolCallerConfiguration<TOOLS extends ToolSet>({\n  tools,\n  toolCallers,\n}: {\n  tools: TOOLS | undefined;\n  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      }","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/ai/src/generate-text/tool-caller-configuration.ts#L23-L59","documentation":"When resolving `experimental_toolCallers`, every key in the toolCallers map must correspond to a tool actually defined in the request's `tools`. resolveToolCallerConfiguration throws this InvalidArgumentError if a toolCallers key references a tool name that does not exist (own-property check, so inherited names like 'constructor' don't count). This is an upfront configuration validation to fail fast instead of silently ignoring routing rules.","triggerScenarios":"Passing `experimental_toolCallers: { myTool: [...] }` to streamText/generateText where 'myTool' is misspelled, was renamed, or was never added to the `tools` object of the same call.","commonSituations":"Typo or renamed tool after refactoring; defining toolCallers at call sites that share a config object but have different tool sets; dynamically built tool maps where a tool was conditionally omitted; case-sensitivity mismatches in tool names.","solutions":["Make every key of experimental_toolCallers exactly match a key in the tools object of the same call.","Fix typos/renames in the toolCallers map (or in tools).","Derive the toolCallers keys programmatically from the same object used for tools.","Remove stale entries for tools no longer passed in this request."],"exampleFix":"// before\nstreamText({ model, tools: { weather }, experimental_toolCallers: { wether: ['weatherAgent'] } });\n\n// after\nstreamText({ model, tools: { weather }, experimental_toolCallers: { weather: ['weatherAgent'] } });","handlingStrategy":"validation","validationCode":"function validateToolCallers(tools: Record<string, unknown>, toolCallers: Record<string, unknown>) {\n  for (const name of Object.keys(toolCallers)) {\n    if (!Object.prototype.hasOwnProperty.call(tools, name)) {\n      throw new Error(`experimental_toolCallers references unknown tool \"${name}\"`);\n    }\n  }\n}","typeGuard":"function allCallersKnown<K extends string>(\n  tools: Record<K, unknown>,\n  toolCallers: Partial<Record<K, unknown>>\n): toolCallers is Partial<Record<K, unknown>> {\n  return Object.keys(toolCallers).every(k => k in tools);\n}","tryCatchPattern":"try {\n  await generateText({ tools, experimental_toolCallers, ... });\n} catch (error) {\n  if (InvalidArgumentError.isInstance(error) && error.parameter === 'experimental_toolCallers') {\n    console.error('Tool caller config error:', error.message);\n  } else throw error;\n}","preventionTips":["Derive toolCallers keys from the same typed object as tools so TypeScript catches renames.","Keep tool definitions and their caller routing co-located in one module.","Avoid stringly-typed tool names; use const objects/satisfies for keys."],"tags":["tools","configuration","invalid-argument"],"backgroundTag":"unknown-tool-name","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}