mrdoob/three.js · error · NodeError
THREE.TSL: No "ConstNode" found in node graph.
Error message
THREE.TSL: No "ConstNode" found in node graph.
What it means
Thrown by RangeNode.getConstNode() when traversing the minNode/maxNode graph yields no node flagged `isConstNode`. RangeNode relies on constant bounds to generate per-instance data for instanced meshes (object.count > 1); a bound that is a dynamic/uniform/runtime node with no ConstNode leaf cannot be evaluated at build time.
Source
Thrown at src/nodes/geometry/RangeNode.js:115
* @returns {Node} The constant node, if found.
*/
getConstNode( node ) {
let output = null;
node.traverse( n => {
if ( n.isConstNode === true ) {
output = n;
}
} );
if ( output === null ) {
throw new NodeError( 'THREE.TSL: No "ConstNode" found in node graph.', this.stackTrace );
}
return output;
}
setup( builder ) {
const object = builder.object;
let output = null;
if ( object.count > 1 ) {
const minNode = this.getConstNode( this.minNode );
const maxNode = this.getConstNode( this.maxNode );
View on GitHub (pinned to da05705fa3)
Solutions
- Pass literal values or nodes that resolve to ConstNode (e.g. `float(1)`, `vec3(...)`, raw numbers/Colors) as the range bounds.
- If the bound must be dynamic, do not use RangeNode; compute the range in the shader directly with the dynamic node.
- Ensure the RangeNode is only used where object.count > 1 with constant bounds.
Example fix
// before material.colorNode = range( myUniformMin, myUniformMax ); // after material.colorNode = range( new Color( 0x000000 ), new Color( 0xffffff ) );
Defensive patterns
Strategy: type-guard
Validate before calling
function isConstBound( node ) {
let found = false;
node.traverse( n => { if ( n.isConstNode === true ) found = true; } );
return found;
}
// before using range() on an InstancedMesh:
if ( ! isConstBound( minNode ) || ! isConstBound( maxNode ) ) {
throw new Error( 'RangeNode bounds must contain a ConstNode' );
} Type guard
const isConstNode = ( n ) => !! n && n.isConstNode === true;
Prevention
- Pass literal values/Colors/Vectors or float()/vec*() as range bounds.
- Do not feed uniforms or dynamic nodes as RangeNode bounds for instanced use.
- Reserve RangeNode for build-time-constant ranges.
When it happens
Trigger: Calling `range()` (or a node that uses it) on an InstancedMesh where minNode/maxNode is a UniformNode, a runtime expression, or any node whose graph contains no ConstNode; passing a raw number works (it gets wrapped in a ConstNode) but passing an already-wrapped dynamic node does not.
Common situations: Driving per-instance random ranges from a uniform or animated node; reusing a RangeNode built from non-const inputs on a multi-instance object; feeding the output of another node chain as the range bound.
Related errors
- THREE.TSL: `texture( value )` function expects a valid insta
- THREE.IndexNode: Unknown scope: ${scope}
- THREE.FunctionNode: Function is not a GLSL code.
- THREE.TSL: Node element ${ name } is not a function
- THREE.FunctionOverloadingNode: FunctionNode must be a layout
AI-assisted analysis of mrdoob/three.js@da05705fa3 (2026-08-12).
Data as JSON: /api/errors/8c4baf251a92eda3.
Report an issue: GitHub.