n8n-io/n8n · error · InterpreterError

Expression nesting too deep (possible cycle in method chain)

Error message

Expression nesting too deep (possible cycle in method chain)

What it means

The interpreter evaluates expressions recursively and tracks depth in `this.evalDepth`. When depth reaches `MAX_EVAL_DEPTH` (500), it throws `InterpreterError` to prevent stack overflow. This guards against pathological nesting that could hang or crash the Node.js process — the message hints at method chains because chained calls (`a.b().c().d()`) recurse through `evaluate` at each step.

Source

Thrown at packages/@n8n/workflow-sdk/src/ast-interpreter/interpreter.ts:146

					this.sourceCode,
					`'${name}' is a reserved SDK function name and cannot be used as a variable name. ` +
						`Use a different name like 'my${name.charAt(0).toUpperCase() + name.slice(1)}'.`,
				);
			}

			const value = declarator.init ? this.evaluate(declarator.init) : undefined;
			this.variables.set(name, value);
		}
	}

	/**
	 * Evaluate an expression and return its value.
	 */
	private evaluate(node: ESTree.Expression | ESTree.SpreadElement | null): unknown {
		if (node === null) return undefined;

		if (this.evalDepth >= SDKInterpreter.MAX_EVAL_DEPTH) {
			throw new InterpreterError(
				'Expression nesting too deep (possible cycle in method chain)',
				node.loc ?? undefined,
				this.sourceCode,
			);
		}

		this.evalDepth++;
		try {
			return this.evaluateNode(node);
		} finally {
			this.evalDepth--;
		}
	}

	private evaluateNode(node: ESTree.Expression | ESTree.SpreadElement): unknown {
		validateNodeType(node, this.sourceCode);

		switch (node.type) {

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Break the expression into multiple `const` statements to flatten the evaluation tree
  2. If code is generated, cap chain length in the generator and emit intermediate consts
  3. Move bulk data into a separate JSON structure or Code node rather than inlining

Example fix

// before (one giant chain)
workflow({ name: 'X' }).add(n1).to(n2).add(n3).to(n4) /* ... 500+ calls ... */;

// after (flattened with consts)
const wf = workflow({ name: 'X' });
const step1 = wf.add(n1).to(n2);
const step2 = step1.add(n3).to(n4);
Defensive patterns

Strategy: validation

Validate before calling

// Measure AST depth before interpreting
function maxExpressionDepth(ast: ESTree.Node): number {
  let max = 0;
  const visit = (node: any, depth: number) => {
    max = Math.max(max, depth);
    for (const child of Object.values(node)) {
      if (Array.isArray(child)) child.forEach((c) => c?.type && visit(c, depth + 1));
      else if (child?.type) visit(child, depth + 1);
    }
  };
  visit(ast, 0);
  return max;
}

const ast = parseSDKCode(sdkCode);
if (maxExpressionDepth(ast) > 450) {
  // warn or reject before calling interpretSDKCode
}

Try / catch

import { InterpreterError } from '@n8n/workflow-sdk/ast-interpreter/errors';

try {
  interpretSDKCode(sdkCode, sdkFunctions);
} catch (e) {
  if (e instanceof InterpreterError && e.message.includes('nesting too deep')) {
    // suggest breaking the expression into multiple consts
  }
  throw e;
}

Prevention

When it happens

Trigger: Writing an expression nested more than 500 levels deep — e.g., hundreds of chained `.to().add().to()` calls in one expression, or a massively nested object/array literal. Each recursive `evaluate` call increments `evalDepth`; at 500 the guard fires at interpreter.ts:145.

Common situations: Machine-generated SDK code that produces extremely long builder chains; inlining a huge data structure as a literal; deeply nested ternary or logical chains (`a && b && c && ...`).

Related errors


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