n8n-io/n8n · error · Error

Maximum branch depth (${WorkflowBuilderImpl.MAX_BRANCH_DEPTH

Error message

Maximum branch depth (${WorkflowBuilderImpl.MAX_BRANCH_DEPTH}) exceeded while building workflow graph

What it means

A recursion guard inside addBranchToGraph. Each nested branch increments a depth counter; if it reaches MAX_BRANCH_DEPTH (500) the builder throws rather than overflow the stack. The guard exists to detect cycles in branch chains (e.g. a NodeChain that references itself) which would otherwise recurse infinitely while flattening the graph. Under normal use with finite, acyclic chains the counter never approaches 500.

Source

Thrown at packages/@n8n/workflow-sdk/src/workflow-builder.ts:1093

		this._currentNode = chain.tail?.name ?? headNodeName;
		this._currentOutput = 0;

		return this;
	}

	/**
	 * Add a branch to the graph, handling both single nodes and NodeChains.
	 * Returns the name of the first node in the branch (for connection from IF).
	 * @param nameMapping - Optional map from node ID to actual map key (used when nodes are renamed)
	 */
	private addBranchToGraph(
		nodes: Map<string, GraphNode>,
		branch: NodeInstance<string, string, unknown>,
		nameMapping?: Map<string, string>,
	): string {
		// Guard against infinite recursion from cycles in branch chains
		if (this._branchDepth >= WorkflowBuilderImpl.MAX_BRANCH_DEPTH) {
			throw new Error(
				`Maximum branch depth (${WorkflowBuilderImpl.MAX_BRANCH_DEPTH}) exceeded while building workflow graph`,
			);
		}
		this._branchDepth++;
		try {
			return this._addBranchToGraphInner(nodes, branch, nameMapping);
		} finally {
			this._branchDepth--;
		}
	}

	private _addBranchToGraphInner(
		nodes: Map<string, GraphNode>,
		branch: NodeInstance<string, string, unknown>,
		nameMapping?: Map<string, string>,
	): string {
		// Create nameMapping if not passed (tracks node ID -> actual map key for renamed nodes)
		const effectiveNameMapping = nameMapping ?? new Map<string, string>();

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Inspect the branch being added for self-reference or mutual reference between composites; break the cycle.
  2. Audit the code that constructs NodeChains/branches for a missing base case in its loop or recursion.
  3. If the depth is legitimately large, refactor into a flatter structure.

Example fix

// before (buggy loop builds a self-referential chain)
let chain = node({ type: 'Set' });
chain = chain.to(chain); // cycle
workflow.add(trigger({})).to(chain); // throws at depth 500

// after
workflow.add(trigger({})).to(node({ type: 'Set' }));
Defensive patterns

Strategy: validation

Validate before calling

const MAX = 500;
function chainDepth(chain: any, seen = new WeakSet()): number {
  // walk the chain counting nesting, guard against cycles with `seen`
  return depth;
}
if (chainDepth(branch) >= MAX) {
  throw new Error('Branch chain too deep or cyclic');
}

Type guard

function isCyclicReference(branch: any, seen = new WeakSet()): boolean {
  if (typeof branch !== 'object' || branch === null) return false;
  if (seen.has(branch)) return true;
  seen.add(branch);
  return Object.values(branch).some(v => isCyclicReference(v, seen));
}

Try / catch

try {
  wf.add(trigger({})).to(branch);
} catch (e) {
  if (e instanceof Error && /Maximum branch depth/.test(e.message)) {
    // inspect branch for self-reference and break the cycle
  }
  throw e;
}

Prevention

When it happens

Trigger: A NodeChain or composite node whose branch references itself directly or indirectly, creating a cycle; programmatically generated node chains with a loop bug; a plugin that recursively dispatches to itself.

Common situations: Code that builds NodeChains in a loop with a faulty termination condition; composites that accidentally include themselves as members; very deep (but legitimate) nested chains that genuinely exceed 500 levels (rare).

Related errors


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