n8n-io/n8n · info · Error

Checkpoint for runId ${this.runId} has status '${state.statu

Error message

Checkpoint for runId ${this.runId} has status '${state.status}' — crashResume only accepts step checkpoints; use resume() for suspended runs

What it means

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.

Source

Thrown at packages/@n8n/agents/src/runtime/loop/agent-runtime.ts:516

	/**
	 * Durable-log RFC (resilience phase): re-drive a run from a `running`-status
	 * step checkpoint after a process crash. Unlike resume(), there is no
	 * pending tool call to settle — the checkpoint was written at a step
	 * boundary — so the loop re-enters directly at the next model call.
	 * `contextNotes` are appended as user messages before the model call: the
	 * host uses them to surface interrupted tool calls ("effect unverified —
	 * verify before retrying") and undrained steering corrections recovered
	 * from its durable event log. Tool calls are never re-executed mechanically.
	 */
	async crashResume(
		options: { runId: string; contextNotes?: string[] } & ExecutionOptions,
	): Promise<StreamResult> {
		this.runId = options.runId;
		const state = await this.runState.loadForCrashResume(this.runId);
		if (!state) throw new Error(`No checkpoint found for runId: ${this.runId}`);
		if (state.status !== 'running') {
			throw new Error(
				`Checkpoint for runId ${this.runId} has status '${state.status}' — crashResume only accepts step checkpoints; use resume() for suspended runs`,
			);
		}
		// A claimed HITL resume also persists as 'running' but still carries its
		// pending tool calls; re-driving it would skip settling them. Step
		// checkpoints are always written with empty pendingToolCalls.
		if (Object.keys(state.pendingToolCalls).length > 0) {
			throw new Error(
				`Checkpoint for runId ${this.runId} has pending tool calls — crashResume only accepts step checkpoints`,
			);
		}

		const list = AgentMessageList.deserialize(state.messageList);
		this.context.hydrateDeferredToolsFromList(list);
		await hydrateFileParts(list.messages(), this.config.fileStore, {
			threadId: state.persistence?.threadId,
		});

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Add a trigger node (scheduleTrigger, manualTrigger, webhookTrigger, etc.) to make the workflow runnable on its own.
  2. If this is a sub-workflow invoked by a parent, set the allowNoTrigger validation option to suppress the notice.
  3. Confirm the trigger node's type string is recognised as a trigger if you believe one is already present.

Example fix

// before — no trigger
set({ name: 'Do Work', assignments: { assignments: [{ id: '1', name: 'x', value: 1, type: 'number' }] } });

// after
const t = scheduleTrigger({ name: 'Daily' });
t.to(set({ name: 'Do Work', assignments: { assignments: [{ id: '1', name: 'x', value: 1, type: 'number' }] } }));
Defensive patterns

Strategy: validation

Validate before calling

import { isTriggerNodeType } from './trigger-detection';

function hasTrigger(nodes: Array<{ type: string }>): boolean {
  return nodes.some((n) => isTriggerNodeType(n.type));
}

// before validating:
if (!hasTrigger(allNodes) && !validationOptions.allowNoTrigger) {
  // add a trigger, or set allowNoTrigger for sub-workflows
}

Prevention

When it happens

Trigger: None of the workflow's nodes has a type classified as a trigger by isTriggerNodeType, and ctx.validationOptions.allowNoTrigger is falsy.

Common situations: Building a sub-workflow intended to be called by another workflow (use allowNoTrigger); an AI builder omitted the trigger; testing a partial graph.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/b1d1f3b7c444f56b. Report an issue: GitHub.