{"record":{"id":"88cdd40a9736caf2","repo":"vercel/ai","slug":"tool-call-toolcallid-not-found-for-approval-r-88cdd4","errorCode":null,"errorMessage":"Tool call \"${toolCallId}\" not found for approval request \"${approvalId}\".","messagePattern":"Tool call \"(.+?)\" not found for approval request \"(.+?)\"\\.","errorType":"exception","errorClass":"ToolCallNotFoundForApprovalError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/generate-text/convert-language-model-content.ts","lineNumber":175,"sourceCode":"            dynamic: toolCall.dynamic,\n            ...(part.providerMetadata != null\n              ? { providerMetadata: part.providerMetadata }\n              : {}),\n            ...(toolCall.toolMetadata != null\n              ? { toolMetadata: toolCall.toolMetadata }\n              : {}),\n          } as TypedToolResult<TOOLS>);\n        }\n        break;\n      }\n\n      case 'tool-approval-request': {\n        const toolCall = toolCalls.find(\n          toolCall => toolCall.toolCallId === part.toolCallId,\n        );\n\n        if (toolCall == null) {\n          throw new ToolCallNotFoundForApprovalError({\n            toolCallId: part.toolCallId,\n            approvalId: part.approvalId,\n          });\n        }\n\n        contentParts.push({\n          type: 'tool-approval-request' as const,\n          approvalId: part.approvalId,\n          toolCall,\n        });\n        break;\n      }\n    }\n  }\n\n  for (const toolOutput of toolOutputs) {\n    if (toolCallIdsWithApprovalResponses.has(toolOutput.toolCallId)) {\n      toolOutputsWithApprovalResponses.push(toolOutput);","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/ai/src/generate-text/convert-language-model-content.ts#L157-L193","documentation":"Thrown when the model emits a tool-approval-request part whose toolCallId does not match any tool call present in the current step's content. The SDK needs to pair an approval request with its originating tool call, and cannot do so. This indicates a malformed or inconsistent provider response for human-in-the-loop tool approval flows.","triggerScenarios":"Calling generateText/streamText with tool approval (needsApproval) enabled and the provider returns a 'tool-approval-request' content part whose toolCallId is absent from the tool calls collected for that step (e.g. the provider omits or renames the tool-call part, or message history was trimmed so the tool call is missing).","commonSituations":"Custom/proxy providers that mangle tool-call IDs; replaying or editing stored conversation messages so the tool-call part was dropped; streaming interruptions where the tool call part was lost but the approval request still arrived; version mismatches between message formats.","solutions":["Inspect the provider response and the reported toolCallId/approvalId; verify the provider emits the tool-call part before the approval request in the same step.","If you reconstruct or trim messages before resuming approval flows, keep the original tool-call parts with matching toolCallId values.","Upgrade @ai-sdk/* packages to matching versions; message content formats changed across majors.","If using a custom provider or proxy, ensure it passes through provider tool-call IDs unchanged.","Catch the error and skip/synthesize a denial for the orphaned approval request."],"exampleFix":"// before: resuming approval after trimming history\nmessages = messages.filter(m => m.role !== 'assistant' || m.content.some(p => p.type === 'tool-call'));\n// after: keep full assistant step containing both tool-call and tool-approval-request parts\nmessages = trimMessagesButKeepCompleteAssistantSteps(messages);","handlingStrategy":"try-catch","validationCode":"// before resuming an approval flow, verify every approval request has a matching tool call\nfunction hasOrphanedApprovals(messages) {\n  const ids = new Set();\n  for (const m of messages) for (const p of Array.isArray(m.content) ? m.content : [])\n    if (p.type === 'tool-call') ids.add(p.toolCallId);\n  return messages.flatMap(m => Array.isArray(m.content) ? m.content : [])\n    .some(p => p.type === 'tool-approval-request' && !ids.has(p.toolCallId));\n}","typeGuard":"function isToolCallNotFoundForApproval(e) {\n  return typeof e === 'object' && e !== null &&\n    ToolCallNotFoundForApprovalError.isInstance(e);\n}","tryCatchPattern":"try {\n  const result = await generateText({ ... });\n} catch (e) {\n  if (ToolCallNotFoundForApprovalError.isInstance(e)) {\n    console.warn('Orphaned approval', e.approvalId, 'for tool call', e.toolCallId);\n    // drop the orphaned approval and re-run, or synthesize a denial\n  } else throw e;\n}","preventionTips":["Never trim assistant tool-call parts out of message history before resuming approvals.","Keep all @ai-sdk/* packages on the same version.","If using a proxy/custom provider, verify tool-call IDs pass through unchanged.","Persist and replay full assistant steps containing tool calls and approval requests together."],"tags":["tool-approval","human-in-the-loop","provider-response"],"backgroundTag":"tool-call-id-mismatch","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}