remotion-dev/remotion · error

Failed to create LUT vertex array

Error message

Failed to create LUT vertex array

What it means

setupLut() calls gl.createVertexArray() to allocate the VAO used for the fullscreen quad that draws the LUT effect. A null return means the WebGL2 context could not create the vertex array object, so the library throws this Error rather than rendering with broken state.

Solutions

  1. Recover from context loss: listen for 'webglcontextrestored' and call setupLut again
  2. Confirm real WebGL2 support: VAOs are core in WebGL2, so a null result signals context problems
  3. Update browser/GPU drivers if using a virtualized or remote GPU
  4. Reduce GL object pressure (delete unused programs/textures/buffers)

Example fix

// before: no context-loss handling
canvas.addEventListener('webglcontextlost', (e) => e.preventDefault());
// after: re-setup on restore
canvas.addEventListener('webglcontextrestored', () => {
  state = setupLut({ gl, width, height, lutContent });
});
Defensive patterns

Strategy: try-catch

Validate before calling

if (gl.isContextLost() || typeof gl.createVertexArray !== 'function') {
  throw new Error('WebGL2 VAO creation unavailable');
}

Try / catch

try {
  state = setupLut({ gl, width, height, lutContent });
} catch (e) {
  if (e.message === 'Failed to create LUT vertex array') {
    // reinitialize after 'webglcontextrestored'
  }
}

Prevention

When it happens

Trigger: setupLut is invoked while the WebGL2 context is lost or exhausted; the environment's GL implementation (mock, very old driver) returns null from createVertexArray even though WebGL2 declares the API.

Common situations: Context loss during offscreen rendering or long renders; emulated/virtualized GPUs with incomplete WebGL2 support; test environments faking WebGL2.

Related errors


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

Appendix: source

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

	return texture;
};

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

	gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);

	const program = createProgram(gl);
	const vao = gl.createVertexArray();
	if (!vao) {
		throw new Error('Failed to create LUT vertex array');
	}

	const vbo = gl.createBuffer();
	if (!vbo) {
		throw new Error('Failed to create LUT vertex buffer');
	}

	gl.bindVertexArray(vao);
	gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
	gl.bufferData(
		gl.ARRAY_BUFFER,
		new Float32Array([-1, -1, 0, 0, 1, -1, 1, 0, -1, 1, 0, 1, 1, 1, 1, 1]),
		gl.STATIC_DRAW,
	);
	gl.enableVertexAttribArray(0);
	gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 16, 0);
	gl.enableVertexAttribArray(1);
	gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 16, 8);

View on GitHub (pinned to 46a3a6bf13)