{"record":{"id":"702058d829d915bf","repo":"mastra-ai/mastra","slug":"inputdata-toolname","errorCode":null,"errorMessage":"${inputData.toolName}","messagePattern":"\\$\\{inputData\\.toolName\\}","errorType":"exception","errorClass":"ToolNotFoundError","httpStatus":null,"severity":"error","filePath":"packages/core/src/loop/workflows/agentic-execution/tool-call-step.ts","lineNumber":995,"sourceCode":"          const agentBgConfig = agentBgConfigCheck;\n          const managerConfig = readScoped(scopeCtx, BACKGROUND_TASK_MANAGER_CONFIG_KEY, 'backgroundTaskManagerConfig');\n\n          const bgResolved = resolveBackgroundConfig({\n            llmBgOverrides,\n            toolName: inputData.toolName,\n            toolConfig: toolBgConfig,\n            agentConfig: agentBgConfig,\n            managerConfig,\n          });\n\n          if (bgResolved.runInBackground) {\n            // Resolve the tool executor from the current closure\n            const stepTools = (readScoped(scopeCtx, STEP_TOOLS_KEY, 'stepTools') as Tools | undefined) || tools;\n            const resolvedTool =\n              stepTools?.[inputData.toolName] ||\n              Object.values(stepTools || {})?.find((t: any) => 'id' in t && t.id === inputData.toolName);\n            if (!resolvedTool?.execute) {\n              throw new ToolNotFoundError(inputData.toolName);\n            }\n            let backgroundChunkTransformQueue: Promise<void> = Promise.resolve();\n            const emittedReplayedToolCalls = new Set<string>();\n\n            // Create a self-contained background task with per-stream hooks\n            const bgTask = createBackgroundTask(backgroundTaskManager, {\n              toolName: inputData.toolName,\n              toolCallId: inputData.toolCallId,\n              args: args as Record<string, unknown>,\n              agentId,\n              threadId: readScoped(scopeCtx, THREAD_ID_KEY, 'threadId'),\n              resourceId: readScoped(scopeCtx, RESOURCE_ID_KEY, 'resourceId'),\n              timeoutMs: bgResolved.timeoutMs,\n              maxRetries: bgResolved.maxRetries,\n              runId,\n              context: {\n                // Executor — uses the tool from the current closure\n                executor: {","sourceCodeStart":977,"sourceCodeEnd":1013,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/loop/workflows/agentic-execution/tool-call-step.ts#L977-L1013","documentation":"Inside createToolCallStep, when the workflow tries to execute an agent tool call, it resolves the tool by name (or tool id) from the tools available in the current scope; if no matching tool with an execute function exists, ToolNotFoundError(toolName) is thrown with the tool name as the message. This means the referenced tool was not registered on the agent/step (or lacks execute).","triggerScenarios":"The model emits a tool call for a name not present in stepTools/tools (lookup by key or by tool.id fails), or the resolved tool has no execute function (e.g. a tool that only provides input schema, or a read-only/display tool registered without execute). Raised from toolCallStep, readOnlyStep, or step created by createToolCallStep.","commonSituations":"Typo or casing mismatch between the tool name the model emits and the key in the tools object; tool defined dynamically (createTool) without execute; tool registered on the agent but not passed into the workflow step's tools; replaying/legacy runs referencing tools that were renamed or removed; tool id changed while stored thread history still references the old id.","solutions":["Ensure every tool referenced by the agent is passed in the tools option of the workflow/agent step, with the exact name/id the model calls.","Add an execute function to tools declared with createTool (or only include executable tools in the tools map).","Verify key casing: tools object keys must match the tool names emitted by the model.","If the run replays old history, either keep old tool ids registered as aliases or start a fresh thread/run.","Log the available keys of stepTools and compare to the failing toolName in the message."],"exampleFix":"// before\nconst weatherTool = createTool({ id: 'weather', inputSchema: z.object({ city: z.string() }) }); // no execute\nawait createWorkflow(...).then(agentStep({ tools: { weather: weatherTool } }));\n// after\nconst weatherTool = createTool({ id: 'weather', inputSchema: z.object({ city: z.string() }), execute: async ({ context }) => getWeather(context.city) });","handlingStrategy":"validation","validationCode":"const stepTools = tools || {};\nconst resolved = stepTools[inputData.toolName] || Object.values(stepTools).find((t: any) => t?.id === inputData.toolName);\nif (!resolved?.execute) throw new Error(`Tool ${inputData.toolName} missing or has no execute()`);","typeGuard":"function isExecutableTool(t: unknown): t is { execute: (...args: unknown[]) => Promise<unknown>; id?: string } {\n  return !!t && typeof t === 'object' && 'execute' in t && typeof (t as any).execute === 'function';\n}","tryCatchPattern":"try {\n  await workflow.start({ inputData });\n} catch (e) {\n  if (e instanceof ToolNotFoundError || (e instanceof Error && e.message === inputData.toolName)) {\n    console.error(`Tool ${e.message} not registered; available:`, Object.keys(tools));\n  }\n  throw e;\n}","preventionTips":["Only include tools with execute functions in the tools option","Keep tool ids/keys consistent with what the model is told to call","When renaming tools, keep old ids as registered aliases for replayed history","Log available tool names in step setup to ease debugging"],"tags":["workflow","tools","tool-not-found","agent-execution"],"backgroundTag":"tool-not-found","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}