{"record":{"id":"b989b6a901795182","repo":"vercel/ai","slug":"tool-toolcall-toolname-does-not-have-an-execu","errorCode":null,"errorMessage":"Tool \"${toolCall.toolName}\" does not have an execute function. Client-side tools should be filtered before calling executeTool.","messagePattern":"Tool \"(.+?)\" does not have an execute function\\. Client-side tools should be filtered before calling executeTool\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/workflow/src/workflow-agent.ts","lineNumber":2951,"sourceCode":"  messages: LanguageModelV4Prompt,\n): ModelMessage[] {\n  const withoutAssistantToolCall =\n    messages.at(-1)?.role === 'assistant' ? messages.slice(0, -1) : messages;\n  return withoutAssistantToolCall as unknown as ModelMessage[];\n}\n\nasync function executeTool(\n  toolCall: { toolCallId: string; toolName: string; input: unknown },\n  tools: ToolSet,\n  messages: LanguageModelV4Prompt,\n  context?: unknown,\n  download?: DownloadFunction,\n  sandbox?: SandboxSession,\n): Promise<WorkflowToolExecutionResult> {\n  const tool = tools[toolCall.toolName];\n  if (!tool) throw new Error(`Tool \"${toolCall.toolName}\" not found`);\n  if (typeof tool.execute !== 'function') {\n    throw new Error(\n      `Tool \"${toolCall.toolName}\" does not have an execute function. ` +\n        `Client-side tools should be filtered before calling executeTool.`,\n    );\n  }\n  // Input is already parsed and validated by streamModelCall's parseToolCall\n  const parsedInput = toolCall.input;\n  let toolResult: unknown;\n\n  try {\n    // Extract execute function to avoid binding `this` to the tool object.\n    // If we called `tool.execute(...)` directly, JavaScript would bind `this`\n    // to `tool`, which contains non-serializable properties like `inputSchema`.\n    // When the execute function is a workflow step (marked with 'use step'),\n    // the step system captures `this` for serialization, causing failures.\n    const { execute } = tool;\n    toolResult = await execute(parsedInput, {\n      toolCallId: toolCall.toolCallId,\n      // Pass the conversation messages to the tool so it has context about the conversation","sourceCodeStart":2933,"sourceCodeEnd":2969,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/workflow/src/workflow-agent.ts#L2933-L2969","documentation":"executeTool requires the resolved tool to have an `execute` function. Tools without execute (client-side / confirm-style tools) must be filtered out before executeTool is called; encountering one here means an unexecutable tool reached server-side execution.","triggerScenarios":"A tool defined without `execute` (e.g. a tool needing human confirmation or client-side execution) is included in the tools map handed to executeTool, and the model calls it.","commonSituations":"Human-in-the-loop confirmation tools not filtered before the server step; mixing client tools into a server tools registry; constructing tools programmatically and forgetting execute.","solutions":["Filter out tools without an execute function before calling executeTool.","Add an execute implementation to the tool if it should run server-side.","Handle client-side tools in the client layer so the model's call never reaches executeTool."],"exampleFix":"// before\nawait executeTool({ toolCall, tools });\n// after\nconst serverTools = Object.fromEntries(\n  Object.entries(tools).filter(([, t]) => typeof t.execute === 'function'),\n);\nawait executeTool({ toolCall, tools: serverTools });","handlingStrategy":"validation","validationCode":"const executable = Object.values(tools).every(t => typeof t.execute === 'function');\nif (!executable) {\n  // remove or handle tools lacking execute before calling executeTool\n}","typeGuard":"function hasExecute(t: unknown): t is { execute: Function } {\n  return !!t && typeof (t as any).execute === 'function';\n}","tryCatchPattern":"try {\n  await executeTool({ toolCall, tools });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('does not have an execute function')) {\n    // route to client-side handling / confirmation flow\n  } else throw e;\n}","preventionTips":["Maintain separate registries for server-side (execute) and client-side tools.","Filter with hasExecute before invoking executeTool.","Implement confirmation-style tools on the client layer explicitly."],"tags":["tools","workflow","client-side"],"backgroundTag":"missing-tool-execute","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}