mrdoob/three.js · error

THREE.NodeBuilder: Invalid node chaining!

Error message

THREE.NodeBuilder: Invalid node chaining!

What it means

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.

Source

Thrown at src/nodes/core/NodeBuilder.js:960

		}
		*/

		this.chaining.push( node );

	}

	/**
	 * Removes the given node from the internal node chain.
	 *
	 * @param {Node} node - The node to remove.
	 */
	removeChain( node ) {

		const lastChain = this.chaining.pop();

		if ( lastChain !== node ) {

			throw new Error( 'THREE.NodeBuilder: Invalid node chaining!' );

		}

	}

	/**
	 * Returns the native shader method name for a given generic name. E.g.
	 * the method name `textureDimensions` matches the WGSL name but must be
	 * resolved to `textureSize` in GLSL.
	 *
	 * @abstract
	 * @param {string} method - The method name to resolve.
	 * @return {string} The resolved method name.
	 */
	getMethod( method ) {

		return method;

View on GitHub (pinned to da05705fa3)

Solutions

  1. Identify the custom Node whose setup/generate is unbalanced and ensure every code path returns a single node (no early throws that skip cleanup).
  2. Avoid sharing a single node instance across multiple materials/renderers; clone it.
  3. Reproduce against the previous Three.js version to detect a regression, then bisect the node graph.
  4. Report as a library bug if it reproduces with only built-in nodes.
Defensive patterns

Strategy: try-catch

Try / catch

// This is a library/custom-node invariant; wrap the build to capture and report.
try {
  renderer.compileAsync( scene, camera );
} catch ( e ) {
  if ( /Invalid node chaining/.test( e.message ) ) {
    console.error( 'Unbalanced node chain — likely a custom Node setup()/generate() bug', e );
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of mrdoob/three.js@da05705fa3 (2026-08-12). Data as JSON: /api/errors/d580f38340e39b10. Report an issue: GitHub.