{"record":{"id":"eb58562a9ff4257b","repo":"n8n-io/n8n","slug":"checkpoint-for-runid-this-runid-has-pending-too","errorCode":null,"errorMessage":"Checkpoint for runId ${this.runId} has pending tool calls — crashResume only accepts step checkpoints","messagePattern":"Checkpoint for runId (.+?) has pending tool calls — crashResume only accepts step checkpoints","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/@n8n/agents/src/runtime/loop/agent-runtime.ts","lineNumber":524,"sourceCode":"\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,\n\t\t});\n\n\t\tlet abortScope: AgentAbortScope | undefined;\n\t\ttry {\n\t\t\tconst { runId: _rid, contextNotes, ...callerExecOptions } = options;\n\t\t\tconst persisted = state.executionOptions ?? {};\n\t\t\tconst persistedMaxIterations = persisted.maxIterations;\n\t\t\tconst callerMaxIterations = callerExecOptions.maxIterations;\n\t\t\tif (\n\t\t\t\tcallerMaxIterations !== undefined &&","sourceCodeStart":506,"sourceCodeEnd":542,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/loop/agent-runtime.ts#L506-L542","documentation":"Critical error thrown when the workflow graph is empty — zero nodes. An empty workflow cannot be executed or serialized meaningfully, so validation fails fast.","triggerScenarios":"ctx.nodes.size === 0 at validateWorkflow time. The validator runs at high priority early specifically to catch this.","commonSituations":"A builder script that constructed a WorkflowBuilder but never added nodes before validating; an AI agent emitted only connections/metadata; a programmatic generation pipeline returned an empty graph on error.","solutions":["Add at least one node (typically a trigger) before validating.","Check the upstream generation step that produced an empty node list — it usually signals an earlier failure.","Guard your generation code to refuse to emit a workflow with no nodes."],"exampleFix":"// before\nconst wf = new WorkflowBuilder();\n// ...nothing added...\nwf.validate();\n\n// after\nconst wf = new WorkflowBuilder();\nconst t = wf.add(manualTrigger({ name: 'Start' }));\nt.to(set({ name: 'Init', assignments: { assignments: [] } }));\nwf.validate();","handlingStrategy":"validation","validationCode":"function assertNonEmpty(nodes: Array<unknown>): void {\n  if (nodes.length === 0) throw new Error('Refusing to build a workflow with no nodes.');\n}\n\n// before validating:\nassertNonEmpty([...builder.nodes]);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Guard generation pipelines to never return an empty node list.","Treat an empty graph from an upstream step as a hard error, not a fallback.","Add at least one trigger before calling validate."],"tags":["workflow-structure","validation","empty-workflow"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}