n8n-io/n8n · error · Error

Cannot decrease maxIterations when resuming a run. Expected…

Error message

Cannot decrease maxIterations when resuming a run. Expected >= ${persistedMaxIterations}, received ${callerMaxIterations}.

What it means

Thrown by the max-nodes validator when the number of instances of a single node type exceeds the maxNodes limit declared in that node type's description. Several n8n node types are singletons (e.g. one Schedule Trigger per workflow) and declare maxNodes=1; exceeding it is a structural error.

Solutions

  1. Remove the extra instance(s) of the node type until count is within maxNodes.
  2. If you genuinely need parallel schedules or branches, restructure to use a single trigger feeding into a branch/merge instead of duplicate singleton nodes.
  3. Confirm the node type's maxNodes value via its description if you believe the limit is wrong.

Example fix

// before — two Schedule Trigger nodes in one workflow
scheduleTrigger({ name: 'Every Hour' });
scheduleTrigger({ name: 'Every Day' });

// after — one trigger, branch with a Switch/IF on time
const trigger = scheduleTrigger({ name: 'Run' });
// route to hourly vs daily logic downstream
Defensive patterns

Strategy: validation

Validate before calling

function countByType(nodes: Array<{ type: string }>): Map<string, number> {
  const m = new Map<string, number>();
  for (const n of nodes) m.set(n.type, (m.get(n.type) ?? 0) + 1);
  return m;
}

// before validating, check against each node type's maxNodes:
for (const [type, count] of countByType(allNodes)) {
  const max = nodeTypesProvider.getByNameAndVersion(type, version)?.description?.maxNodes;
  if (max !== undefined && count > max) throw new Error(`${type}: ${count} exceeds maxNodes ${max}`);
}

Prevention

When it happens

Trigger: After grouping nodes by type (count > 1), the validator resolves the first instance's version, looks up nodeType.description.maxNodes via the nodeTypesProvider, and fires when count > maxNodes. Only types that actually declare a numeric maxNodes are constrained.

Common situations: Adding a second Schedule Trigger or other singleton node; merging two workflows that each contained the same limited node type; an AI builder duplicating a trigger because it misread the requirement.

Related errors


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

Appendix: source

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

		}

		try {
			// Merge persisted execution options with fresh caller options
			const {
				runId: _rid,
				toolCallId: _tcid,
				onResumeClaimed: _onResumeClaimed,
				...callerExecOptions
			} = options;
			const persisted = state.executionOptions ?? {};
			const persistedMaxIterations = persisted.maxIterations;
			const callerMaxIterations = callerExecOptions.maxIterations;
			if (
				callerMaxIterations !== undefined &&
				persistedMaxIterations !== undefined &&
				callerMaxIterations < persistedMaxIterations
			) {
				throw new Error(
					`Cannot decrease maxIterations when resuming a run. Expected >= ${persistedMaxIterations}, received ${callerMaxIterations}.`,
				);
			}

			const mergedMaxIterations = callerMaxIterations ?? persistedMaxIterations;
			const mergedExecOptions: ExecutionOptions & { iterationCount?: number } = {
				...callerExecOptions,
				...(mergedMaxIterations !== undefined ? { maxIterations: mergedMaxIterations } : {}),
				...(state.iterationCount !== undefined ? { iterationCount: state.iterationCount } : {}),
			};

			const resumeOptions: RuntimeExecutionOptions = {
				persistence: state.persistence,
				...mergedExecOptions,
			};

			const claimed = await this.runState.claimResume(this.runId, state);
			if (!claimed) {

View on GitHub (pinned to 5ac6606e81)