{"record":{"id":"f40046ee7df3db98","repo":"n8n-io/n8n","slug":"maximum-branch-depth-workflowbuilderimpl-max-br","errorCode":null,"errorMessage":"Maximum branch depth (${WorkflowBuilderImpl.MAX_BRANCH_DEPTH}) exceeded while building workflow graph","messagePattern":"Maximum branch depth \\((.+?)\\) exceeded while building workflow graph","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/workflow-sdk/src/workflow-builder.ts","lineNumber":1093,"sourceCode":"\t\tthis._currentNode = chain.tail?.name ?? headNodeName;\n\t\tthis._currentOutput = 0;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a branch to the graph, handling both single nodes and NodeChains.\n\t * Returns the name of the first node in the branch (for connection from IF).\n\t * @param nameMapping - Optional map from node ID to actual map key (used when nodes are renamed)\n\t */\n\tprivate addBranchToGraph(\n\t\tnodes: Map<string, GraphNode>,\n\t\tbranch: NodeInstance<string, string, unknown>,\n\t\tnameMapping?: Map<string, string>,\n\t): string {\n\t\t// Guard against infinite recursion from cycles in branch chains\n\t\tif (this._branchDepth >= WorkflowBuilderImpl.MAX_BRANCH_DEPTH) {\n\t\t\tthrow new Error(\n\t\t\t\t`Maximum branch depth (${WorkflowBuilderImpl.MAX_BRANCH_DEPTH}) exceeded while building workflow graph`,\n\t\t\t);\n\t\t}\n\t\tthis._branchDepth++;\n\t\ttry {\n\t\t\treturn this._addBranchToGraphInner(nodes, branch, nameMapping);\n\t\t} finally {\n\t\t\tthis._branchDepth--;\n\t\t}\n\t}\n\n\tprivate _addBranchToGraphInner(\n\t\tnodes: Map<string, GraphNode>,\n\t\tbranch: NodeInstance<string, string, unknown>,\n\t\tnameMapping?: Map<string, string>,\n\t): string {\n\t\t// Create nameMapping if not passed (tracks node ID -> actual map key for renamed nodes)\n\t\tconst effectiveNameMapping = nameMapping ?? new Map<string, string>();","sourceCodeStart":1075,"sourceCodeEnd":1111,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/workflow-sdk/src/workflow-builder.ts#L1075-L1111","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Inspect the branch being added for self-reference or mutual reference between composites; break the cycle.","Audit the code that constructs NodeChains/branches for a missing base case in its loop or recursion.","If the depth is legitimately large, refactor into a flatter structure."],"exampleFix":"// before (buggy loop builds a self-referential chain)\nlet chain = node({ type: 'Set' });\nchain = chain.to(chain); // cycle\nworkflow.add(trigger({})).to(chain); // throws at depth 500\n\n// after\nworkflow.add(trigger({})).to(node({ type: 'Set' }));","handlingStrategy":"validation","validationCode":"const MAX = 500;\nfunction chainDepth(chain: any, seen = new WeakSet()): number {\n  // walk the chain counting nesting, guard against cycles with `seen`\n  return depth;\n}\nif (chainDepth(branch) >= MAX) {\n  throw new Error('Branch chain too deep or cyclic');\n}","typeGuard":"function isCyclicReference(branch: any, seen = new WeakSet()): boolean {\n  if (typeof branch !== 'object' || branch === null) return false;\n  if (seen.has(branch)) return true;\n  seen.add(branch);\n  return Object.values(branch).some(v => isCyclicReference(v, seen));\n}","tryCatchPattern":"try {\n  wf.add(trigger({})).to(branch);\n} catch (e) {\n  if (e instanceof Error && /Maximum branch depth/.test(e.message)) {\n    // inspect branch for self-reference and break the cycle\n  }\n  throw e;\n}","preventionTips":["Never construct a NodeChain that references itself or its ancestor.","Audit loop/recursion that builds chains for a missing base case.","Flatten deeply nested legitimate chains below 500 levels."],"tags":["workflow-builder","recursion","cycle-detection","graph"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}