{"record":{"id":"d580f38340e39b10","repo":"mrdoob/three.js","slug":"three-nodebuilder-invalid-node-chaining","errorCode":null,"errorMessage":"THREE.NodeBuilder: Invalid node chaining!","messagePattern":"THREE\\.NodeBuilder: Invalid node chaining!","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/nodes/core/NodeBuilder.js","lineNumber":960,"sourceCode":"\t\t}\n\t\t*/\n\n\t\tthis.chaining.push( node );\n\n\t}\n\n\t/**\n\t * Removes the given node from the internal node chain.\n\t *\n\t * @param {Node} node - The node to remove.\n\t */\n\tremoveChain( node ) {\n\n\t\tconst lastChain = this.chaining.pop();\n\n\t\tif ( lastChain !== node ) {\n\n\t\t\tthrow new Error( 'THREE.NodeBuilder: Invalid node chaining!' );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Returns the native shader method name for a given generic name. E.g.\n\t * the method name `textureDimensions` matches the WGSL name but must be\n\t * resolved to `textureSize` in GLSL.\n\t *\n\t * @abstract\n\t * @param {string} method - The method name to resolve.\n\t * @return {string} The resolved method name.\n\t */\n\tgetMethod( method ) {\n\n\t\treturn method;\n","sourceCodeStart":942,"sourceCodeEnd":978,"githubUrl":"https://github.com/mrdoob/three.js/blob/da05705fa31f02710c19772a2aa70c7454807f0c/src/nodes/core/NodeBuilder.js#L942-L978","documentation":"An internal NodeBuilder invariant: `removeChain(node)` pops the last entry of the `chaining` stack and asserts it is the same node passed in. A mismatch means the node-graph traversal pushed and popped chain entries out of order. This is almost always a library or custom-Node bug in `setup()`/`generate()` that calls builder flow methods asymmetrically, not a user configuration error.","triggerScenarios":"A custom Node whose `generate()`/`setup()` returns early or throws partway, leaving the chain unbalanced; a node that manually calls `builder.addChain()` without a matching `removeChain()`; corrupted node reuse across builders; race conditions from sharing mutable node instances between simultaneous builds.","commonSituations":"Authoring a custom TSL node and forgetting to return the proper node from an `Fn` body; mutating a node tree while it is being built; cloning/caching a node that holds builder-specific chain state; version regression after upgrading Three.js node internals.","solutions":["Identify the custom Node whose setup/generate is unbalanced and ensure every code path returns a single node (no early throws that skip cleanup).","Avoid sharing a single node instance across multiple materials/renderers; clone it.","Reproduce against the previous Three.js version to detect a regression, then bisect the node graph.","Report as a library bug if it reproduces with only built-in nodes."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// This is a library/custom-node invariant; wrap the build to capture and report.\ntry {\n  renderer.compileAsync( scene, camera );\n} catch ( e ) {\n  if ( /Invalid node chaining/.test( e.message ) ) {\n    console.error( 'Unbalanced node chain — likely a custom Node setup()/generate() bug', e );\n  } else throw e;\n}","preventionTips":["In custom nodes, ensure every setup()/generate() path returns a node and never throws mid-flow leaving the chain pushed.","Never share a node instance across concurrent builds; clone it.","Bisect against the previous Three.js version to detect regressions."],"tags":["node-builder","internal-invariant","custom-node"],"backgroundTag":null,"analyzedSha":"da05705fa31f02710c19772a2aa70c7454807f0c","analyzedAt":"2026-08-12T22:51:37.160Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}