mrdoob/three.js · error · Error

THREE.WebGLProgram: Can not resolve #include <' + include +

Error message

THREE.WebGLProgram: Can not resolve #include <' + include + '>

What it means

Thrown by WebGLProgram's resolveIncludes() when a `#include <name>` directive in a shader references a chunk not found in the ShaderChunk registry and not present in the deprecation shaderChunkMap. Three injects shader snippets by name at build time; an unresolved name means the chunk is missing, misspelled, or removed.

Source

Thrown at src/renderers/webgl/WebGLProgram.js:268

const shaderChunkMap = new Map();

function includeReplacer( match, include ) {

	let string = ShaderChunk[ include ];

	if ( string === undefined ) {

		const newInclude = shaderChunkMap.get( include );

		if ( newInclude !== undefined ) {

			string = ShaderChunk[ newInclude ];
			warn( 'WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.', include, newInclude );

		} else {

			throw new Error( 'THREE.WebGLProgram: Can not resolve #include <' + include + '>' );

		}

	}

	return resolveIncludes( string );

}

// Unroll Loops

const unrollLoopPattern = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;

function unrollLoops( string ) {

	return string.replace( unrollLoopPattern, loopReplacer );

}

View on GitHub (pinned to da05705fa3)

Solutions

  1. Check the chunk name against `THREE.ShaderChunk` keys (and ShaderLib) for the installed three.js version.
  2. Replace removed/renamed chunks with the current equivalents; consult the migration notes for the version you upgraded across.
  3. If the chunk is custom, register it first: `THREE.ShaderChunk.mychunk = '...glsl...';`.
  4. Remove the `#include` and inline the GLSL manually if no equivalent chunk exists.

Example fix

// before
material = new THREE.ShaderMaterial({
  fragmentShader: '#include <tonemapping_fragment_old>' // removed/renamed
});

// after
material = new THREE.ShaderMaterial({
  fragmentShader: '#include <tonemapping_fragment>' // current name
});
Defensive patterns

Strategy: try-catch

Validate before calling

function validateIncludes(glsl, ShaderChunk) {
  const re = /#include\s*<([\w-]+)>/g;
  let m, missing = [];
  while ((m = re.exec(glsl))) if (!(m[1] in ShaderChunk)) missing.push(m[1]);
  if (missing.length) throw new Error('Unknown shader chunks: ' + missing.join(', '));
}

Type guard

function allIncludesResolve(glsl, ShaderChunk) {
  const re = /#include\s*<([\w-]+)>/g;
  let m;
  while ((m = re.exec(glsl))) if (!(m[1] in ShaderChunk)) return false;
  return true;
}

Try / catch

try {
  material = new THREE.ShaderMaterial({ fragmentShader });
} catch (e) {
  if (/Can not resolve #include/.test(e.message)) { /* list ShaderLib keys, fix chunk name, rebuild */ }
  else throw e;
}

Prevention

When it happens

Trigger: Using ShaderMaterial/RTM with a custom shader string containing `#include <foo>` where 'foo' is not a registered ShaderChunk. Referring to a chunk that was renamed or removed in a three.js upgrade. Typing a chunk name wrong.

Common situations: Upgrading three.js and using a chunk that was renamed/deprecated beyond the deprecation map's coverage. Hand-written GLSL referencing internal chunks that no longer exist. Custom shaders ported from examples that depended on private chunks. Misspelling common chunks like `#include <encodings_fragment>` vs the current name.

Related errors


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