{"record":{"id":"f1a56a00bf5eed92","repo":"n8n-io/n8n","slug":"no-checkpoint-found-for-runid-this-runid","errorCode":null,"errorMessage":"No checkpoint found for runId: ${this.runId}","messagePattern":"No checkpoint found for runId: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/@n8n/agents/src/runtime/loop/agent-runtime.ts","lineNumber":514,"sourceCode":"\t\t}\n\t}\n\n\t/**\n\t * Durable-log RFC (resilience phase): re-drive a run from a `running`-status\n\t * step checkpoint after a process crash. Unlike resume(), there is no\n\t * pending tool call to settle — the checkpoint was written at a step\n\t * boundary — so the loop re-enters directly at the next model call.\n\t * `contextNotes` are appended as user messages before the model call: the\n\t * host uses them to surface interrupted tool calls (\"effect unverified —\n\t * verify before retrying\") and undrained steering corrections recovered\n\t * from its durable event log. Tool calls are never re-executed mechanically.\n\t */\n\tasync crashResume(\n\t\toptions: { runId: string; contextNotes?: string[] } & ExecutionOptions,\n\t): Promise<StreamResult> {\n\t\tthis.runId = options.runId;\n\t\tconst state = await this.runState.loadForCrashResume(this.runId);\n\t\tif (!state) throw new Error(`No checkpoint found for runId: ${this.runId}`);\n\t\tif (state.status !== 'running') {\n\t\t\tthrow new Error(\n\t\t\t\t`Checkpoint for runId ${this.runId} has status '${state.status}' — crashResume only accepts step checkpoints; use resume() for suspended runs`,\n\t\t\t);\n\t\t}\n\t\t// A claimed HITL resume also persists as 'running' but still carries its\n\t\t// pending tool calls; re-driving it would skip settling them. Step\n\t\t// checkpoints are always written with empty pendingToolCalls.\n\t\tif (Object.keys(state.pendingToolCalls).length > 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Checkpoint for runId ${this.runId} has pending tool calls — crashResume only accepts step checkpoints`,\n\t\t\t);\n\t\t}\n\n\t\tconst list = AgentMessageList.deserialize(state.messageList);\n\t\tthis.context.hydrateDeferredToolsFromList(list);\n\t\tawait hydrateFileParts(list.messages(), this.config.fileStore, {\n\t\t\tthreadId: state.persistence?.threadId,","sourceCodeStart":496,"sourceCodeEnd":532,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/loop/agent-runtime.ts#L496-L532","documentation":"Thrown by the merge-node validator when a Merge node has fewer than two distinct connected inputs. Merge nodes combine multiple branches, so a single input defeats their purpose and usually indicates a wiring mistake (e.g. both branches connected to the same input index).","triggerScenarios":"The validator collects the set of distinct inputIndex/targetInputIndex values across all connections targeting the Merge node; if connectedInputIndices.size < 2, the issue fires. This happens when no connection targets input 1, or every connection targets the same input index.","commonSituations":"Both upstream branches are wired with .to(mergeNode) (defaults to input 0); an AI builder forgets to specify .input(1); a branch was deleted leaving only one input connected.","solutions":["Wire each upstream branch to a distinct input: .to(mergeNode.input(0)) and .to(mergeNode.input(1)).","Verify the Merge node's input mode (append/combine/chooseBranch) matches how many branches you connect.","If only one branch is genuinely needed, remove the Merge node and connect the branch directly."],"exampleFix":"// before — both branches land on input 0\nbranchA.to(mergeNode);\nbranchB.to(mergeNode);\n\n// after\nbranchA.to(mergeNode.input(0));\nbranchB.to(mergeNode.input(1));","handlingStrategy":"validation","validationCode":"function distinctInputCount(connections: Array<{ target?: { inputIndex?: number }; targetInputIndex?: number }>): number {\n  const set = new Set<number>();\n  for (const c of connections) {\n    const idx = c.target?.inputIndex ?? c.targetInputIndex ?? 0;\n    set.add(idx);\n  }\n  return set.size;\n}\n\n// before validating a Merge node:\nif (distinctInputCount(incomingConnections) < 2) {\n  throw new Error('Merge node needs >= 2 distinct inputs; use .to(merge.input(0)) and .to(merge.input(1)).');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always specify the input index when connecting to a Merge node.","Visually confirm two distinct branches enter the Merge in the canvas.","Delete the Merge node if only one branch is needed."],"tags":["workflow-structure","merge-node","connections"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}