remotion-dev/remotion · error

Failed to create LUT texture

Error message

Failed to create LUT texture

What it means

createLutTexture() calls gl.createTexture() to allocate the TEXTURE_3D object that holds the parsed .cube LUT data. If the WebGL2 implementation returns null it cannot create the texture object, and this Error is thrown. As with other null returns from gl.create*, throwing early prevents operating on an invalid handle.

Solutions

  1. Handle 'webglcontextlost'/'webglcontextrestored' events and re-run setupLut after restore
  2. Verify the environment provides a real WebGL2 context (not a partial mock)
  3. Reduce simultaneous GL texture usage in the composition
  4. Recreate the canvas/context if the context remains lost

Example fix

// before: mock GL in tests
const gl = { createTexture: () => null };
// after: use a real WebGL2 context
const gl = canvas.getContext('webgl2');
if (!gl) throw new Error('WebGL2 unavailable');
Defensive patterns

Strategy: try-catch

Validate before calling

if (gl.isContextLost()) {
  throw new Error('WebGL context lost; cannot create LUT texture');
}

Try / catch

try {
  state = setupLut({ gl, width, height, lutContent });
} catch (e) {
  if (e.message === 'Failed to create LUT texture') {
    // recreate context or degrade gracefully without the effect
  }
}

Prevention

When it happens

Trigger: setupLut runs on a lost or resource-starved WebGL2 context; the browser/driver fails to allocate 3D texture objects; a stub/gl-mock returns null from createTexture.

Common situations: GPU reset during a long render; mobile browsers evicting GL resources; unit tests running against fake WebGL contexts that return null instead of real handles.

Related errors


AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18). Data as JSON: /api/errors/2c2fe986e32f8cbd. Report an issue: GitHub.

Appendix: source

Thrown at packages/effects/src/lut/lut-runtime.ts:90

const createSourceTexture = (gl: WebGL2RenderingContext): WebGLTexture => {
	const texture = gl.createTexture();
	if (!texture) {
		throw new Error('Failed to create LUT source texture');
	}

	gl.bindTexture(gl.TEXTURE_2D, texture);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
	gl.bindTexture(gl.TEXTURE_2D, null);
	return texture;
};

const createLutTexture = (gl: WebGL2RenderingContext): WebGLTexture => {
	const texture = gl.createTexture();
	if (!texture) {
		throw new Error('Failed to create LUT texture');
	}

	gl.bindTexture(gl.TEXTURE_3D, texture);
	gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
	gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
	gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
	gl.texParameteri(gl.TEXTURE_3D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE);
	gl.bindTexture(gl.TEXTURE_3D, null);
	return texture;
};

export const setupLut = (target: HTMLCanvasElement): LutState => {
	const gl = target.getContext('webgl2', {
		premultipliedAlpha: true,
		alpha: true,
		preserveDrawingBuffer: true,
	});

View on GitHub (pinned to 46a3a6bf13)