{"record":{"id":"85c31483e5dd339b","repo":"n8n-io/n8n","slug":"expression-nesting-too-deep-possible-cycle-in-met","errorCode":null,"errorMessage":"Expression nesting too deep (possible cycle in method chain)","messagePattern":"Expression nesting too deep \\(possible cycle in method chain\\)","errorType":"exception","errorClass":"InterpreterError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/workflow-sdk/src/ast-interpreter/interpreter.ts","lineNumber":146,"sourceCode":"\t\t\t\t\tthis.sourceCode,\n\t\t\t\t\t`'${name}' is a reserved SDK function name and cannot be used as a variable name. ` +\n\t\t\t\t\t\t`Use a different name like 'my${name.charAt(0).toUpperCase() + name.slice(1)}'.`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst value = declarator.init ? this.evaluate(declarator.init) : undefined;\n\t\t\tthis.variables.set(name, value);\n\t\t}\n\t}\n\n\t/**\n\t * Evaluate an expression and return its value.\n\t */\n\tprivate evaluate(node: ESTree.Expression | ESTree.SpreadElement | null): unknown {\n\t\tif (node === null) return undefined;\n\n\t\tif (this.evalDepth >= SDKInterpreter.MAX_EVAL_DEPTH) {\n\t\t\tthrow new InterpreterError(\n\t\t\t\t'Expression nesting too deep (possible cycle in method chain)',\n\t\t\t\tnode.loc ?? undefined,\n\t\t\t\tthis.sourceCode,\n\t\t\t);\n\t\t}\n\n\t\tthis.evalDepth++;\n\t\ttry {\n\t\t\treturn this.evaluateNode(node);\n\t\t} finally {\n\t\t\tthis.evalDepth--;\n\t\t}\n\t}\n\n\tprivate evaluateNode(node: ESTree.Expression | ESTree.SpreadElement): unknown {\n\t\tvalidateNodeType(node, this.sourceCode);\n\n\t\tswitch (node.type) {","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/workflow-sdk/src/ast-interpreter/interpreter.ts#L128-L164","documentation":"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.","triggerScenarios":"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.","commonSituations":"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 && ...`).","solutions":["Break the expression into multiple `const` statements to flatten the evaluation tree","If code is generated, cap chain length in the generator and emit intermediate consts","Move bulk data into a separate JSON structure or Code node rather than inlining"],"exampleFix":"// before (one giant chain)\nworkflow({ name: 'X' }).add(n1).to(n2).add(n3).to(n4) /* ... 500+ calls ... */;\n\n// after (flattened with consts)\nconst wf = workflow({ name: 'X' });\nconst step1 = wf.add(n1).to(n2);\nconst step2 = step1.add(n3).to(n4);","handlingStrategy":"validation","validationCode":"// Measure AST depth before interpreting\nfunction maxExpressionDepth(ast: ESTree.Node): number {\n  let max = 0;\n  const visit = (node: any, depth: number) => {\n    max = Math.max(max, depth);\n    for (const child of Object.values(node)) {\n      if (Array.isArray(child)) child.forEach((c) => c?.type && visit(c, depth + 1));\n      else if (child?.type) visit(child, depth + 1);\n    }\n  };\n  visit(ast, 0);\n  return max;\n}\n\nconst ast = parseSDKCode(sdkCode);\nif (maxExpressionDepth(ast) > 450) {\n  // warn or reject before calling interpretSDKCode\n}","typeGuard":null,"tryCatchPattern":"import { InterpreterError } from '@n8n/workflow-sdk/ast-interpreter/errors';\n\ntry {\n  interpretSDKCode(sdkCode, sdkFunctions);\n} catch (e) {\n  if (e instanceof InterpreterError && e.message.includes('nesting too deep')) {\n    // suggest breaking the expression into multiple consts\n  }\n  throw e;\n}","preventionTips":["Avoid chains longer than ~100 method calls in one expression","Flatten generated code into intermediate `const` statements","If generating SDK code, cap expression depth in the generator configuration"],"tags":["sdk-interpreter","recursion-depth","performance","stack-overflow"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}