{"record":{"id":"b1d1f3b7c444f56b","repo":"n8n-io/n8n","slug":"checkpoint-for-runid-this-runid-has-status-s","errorCode":null,"errorMessage":"Checkpoint for runId ${this.runId} has status '${state.status}' — crashResume only accepts step checkpoints; use resume() for suspended runs","messagePattern":"Checkpoint for runId (.+?) has status '(.+?)' — crashResume only accepts step checkpoints; use resume\\(\\) for suspended runs","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"packages/@n8n/agents/src/runtime/loop/agent-runtime.ts","lineNumber":516,"sourceCode":"\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,\n\t\t});\n","sourceCodeStart":498,"sourceCodeEnd":534,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/agents/src/runtime/loop/agent-runtime.ts#L498-L534","documentation":"Informational notice that the workflow contains no trigger node, so it can only be run manually. This is allowed but usually unintended for production workflows. Suppressed entirely when validationOptions.allowNoTrigger is set.","triggerScenarios":"None of the workflow's nodes has a type classified as a trigger by isTriggerNodeType, and ctx.validationOptions.allowNoTrigger is falsy.","commonSituations":"Building a sub-workflow intended to be called by another workflow (use allowNoTrigger); an AI builder omitted the trigger; testing a partial graph.","solutions":["Add a trigger node (scheduleTrigger, manualTrigger, webhookTrigger, etc.) to make the workflow runnable on its own.","If this is a sub-workflow invoked by a parent, set the allowNoTrigger validation option to suppress the notice.","Confirm the trigger node's type string is recognised as a trigger if you believe one is already present."],"exampleFix":"// before — no trigger\nset({ name: 'Do Work', assignments: { assignments: [{ id: '1', name: 'x', value: 1, type: 'number' }] } });\n\n// after\nconst t = scheduleTrigger({ name: 'Daily' });\nt.to(set({ name: 'Do Work', assignments: { assignments: [{ id: '1', name: 'x', value: 1, type: 'number' }] } }));","handlingStrategy":"validation","validationCode":"import { isTriggerNodeType } from './trigger-detection';\n\nfunction hasTrigger(nodes: Array<{ type: string }>): boolean {\n  return nodes.some((n) => isTriggerNodeType(n.type));\n}\n\n// before validating:\nif (!hasTrigger(allNodes) && !validationOptions.allowNoTrigger) {\n  // add a trigger, or set allowNoTrigger for sub-workflows\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set allowNoTrigger only for genuinely callable sub-workflows.","Make trigger addition the first step of workflow generation.","Confirm the trigger type string is classified as a trigger."],"tags":["workflow-structure","trigger","informational"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}