{"record":{"id":"b154205c6f15bf89","repo":"n8n-io/n8n","slug":"no-pending-tool-call-found-for-toolcallid-resum","errorCode":null,"errorMessage":"No pending tool call found for toolCallId: ${resumedId}","messagePattern":"No pending tool call found for toolCallId: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/agents/src/runtime/tools/tool-call-executor.ts","lineNumber":501,"sourceCode":"\t * Returns a `ToolCallBatchResult` — the caller handles persistence.\n\t */\n\tasync iteratePendingToolCallsConcurrent(\n\t\tctx: ToolBatchContext & { pendingResume: PendingResume },\n\t): Promise<ToolCallBatchResult> {\n\t\tconst {\n\t\t\tpendingResume,\n\t\t\ttoolMap,\n\t\t\tlist,\n\t\t\trunId,\n\t\t\tpersistence,\n\t\t\ttelemetry: resolvedTelemetry,\n\t\t\texecutionCounter,\n\t\t\tabortSignal,\n\t\t} = ctx;\n\t\tconst resumedId = pendingResume.resumeToolCallId;\n\t\tconst resumedEntry = pendingResume.pendingToolCalls[resumedId];\n\t\tif (!resumedEntry) {\n\t\t\tthrow new Error(`No pending tool call found for toolCallId: ${resumedId}`);\n\t\t}\n\n\t\tconst resumedToolName = resumedEntry.toolName;\n\t\tconst results: ToolCallSuccess[] = [];\n\t\tconst suspensions: ToolCallSuspension[] = [];\n\t\tconst errors: ToolCallError[] = [];\n\t\tconst pending: Record<string, PendingToolCall> = {};\n\n\t\t// 1. Execute the resumed tool\n\t\tconst processResult = await this.processToolCall(\n\t\t\tthis.pendingToolCallParams(resumedEntry, ctx, pendingResume.resumeData),\n\t\t);\n\n\t\tif (processResult.outcome === 'suspended') {\n\t\t\tpending[resumedId] = {\n\t\t\t\tsuspended: true,\n\t\t\t\ttoolCallId: resumedEntry.toolCallId,\n\t\t\t\ttoolName: resumedToolName,","sourceCodeStart":483,"sourceCodeEnd":519,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/tools/tool-call-executor.ts#L483-L519","documentation":"Thrown during tool-call resume when pendingResume.pendingToolCalls does not contain an entry for the resumeToolCallId. The resume path expects to find the previously-suspended tool call in the pending map so it can re-execute it with the resume data. A missing entry means the checkpoint referenced a tool call that was never suspended or has already been completed/cleaned up.","triggerScenarios":"Resuming an execution with a resumeToolCallId that does not match any key in pendingToolCalls. This happens if the checkpoint store returns a stale or already-consumed resume token, if the tool call was completed in a separate resume attempt, or if the pending map was reconstructed incorrectly from persistence.","commonSituations":"Double-resume: the same suspended execution is resumed twice (the first resume consumed the pending entry). Checkpoint store does not enforce single-consumer semantics. Race condition where two resume requests hit the same execution. Checkpoint data corruption or schema migration that lost the pending tool call map.","solutions":["Ensure each suspended execution is resumed exactly once — use idempotency keys or a lock on the execution ID.","Verify the checkpoint store correctly persists and restores the pendingToolCalls map.","If the resume token is stale, start a fresh execution instead of resuming.","Add logging to capture the available pendingToolCallIds vs the requested resumeToolCallId at resume time."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"function hasPendingToolCall(pendingToolCalls: Record<string, unknown>, resumeToolCallId: string): boolean {\n  return resumeToolCallId in pendingToolCalls;\n}\nif (!hasPendingToolCall(ctx.pendingToolCalls, resumeData.resumeToolCallId)) {\n  // do not attempt resume; start a new execution\n}","typeGuard":null,"tryCatchPattern":"try {\n  await executor.resume(pendingResume);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('No pending tool call found')) {\n    // resume token is stale; start a fresh execution\n  } else throw e;\n}","preventionTips":["Use idempotency guards or locks to ensure each suspended execution resumes exactly once.","Verify the checkpoint store correctly persists and restores the pendingToolCalls map.","Log available vs requested toolCallIds to diagnose mismatches early."],"tags":["resume","tool-call","checkpoint","concurrency"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}