{"record":{"id":"8c4baf251a92eda3","repo":"mrdoob/three.js","slug":"three-tsl-no-constnode-found-in-node-graph","errorCode":null,"errorMessage":"THREE.TSL: No \"ConstNode\" found in node graph.","messagePattern":"THREE\\.TSL: No \"ConstNode\" found in node graph\\.","errorType":"exception","errorClass":"NodeError","httpStatus":null,"severity":"error","filePath":"src/nodes/geometry/RangeNode.js","lineNumber":115,"sourceCode":"\t * @returns {Node} The constant node, if found.\n\t */\n\tgetConstNode( node ) {\n\n\t\tlet output = null;\n\n\t\tnode.traverse( n => {\n\n\t\t\tif ( n.isConstNode === true ) {\n\n\t\t\t\toutput = n;\n\n\t\t\t}\n\n\t\t} );\n\n\t\tif ( output === null ) {\n\n\t\t\tthrow new NodeError( 'THREE.TSL: No \"ConstNode\" found in node graph.', this.stackTrace );\n\n\t\t}\n\n\t\treturn output;\n\n\t}\n\n\tsetup( builder ) {\n\n\t\tconst object = builder.object;\n\n\t\tlet output = null;\n\n\t\tif ( object.count > 1 ) {\n\n\t\t\tconst minNode = this.getConstNode( this.minNode );\n\t\t\tconst maxNode = this.getConstNode( this.maxNode );\n","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/mrdoob/three.js/blob/da05705fa31f02710c19772a2aa70c7454807f0c/src/nodes/geometry/RangeNode.js#L97-L133","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nmaterial.colorNode = range( myUniformMin, myUniformMax );\n\n// after\nmaterial.colorNode = range( new Color( 0x000000 ), new Color( 0xffffff ) );","handlingStrategy":"type-guard","validationCode":"function isConstBound( node ) {\n  let found = false;\n  node.traverse( n => { if ( n.isConstNode === true ) found = true; } );\n  return found;\n}\n\n// before using range() on an InstancedMesh:\nif ( ! isConstBound( minNode ) || ! isConstBound( maxNode ) ) {\n  throw new Error( 'RangeNode bounds must contain a ConstNode' );\n}","typeGuard":"const isConstNode = ( n ) => !! n && n.isConstNode === true;","tryCatchPattern":null,"preventionTips":["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."],"tags":["range-node","instanced-mesh","tsl","const-node"],"backgroundTag":null,"analyzedSha":"da05705fa31f02710c19772a2aa70c7454807f0c","analyzedAt":"2026-08-12T22:51:37.160Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}