remotion-dev/remotion · error · Error

Failed to create WebGL vertex array

Error message

Failed to create WebGL vertex array

What it means

Thrown by pixelDissolve's createFullscreenQuad helper when gl.createVertexArray() returns null during effect setup. WebGL2 returns null instead of throwing when the GL context is lost, when the implementation runs out of VAO slots, or when the underlying GPU/SwiftShader cannot service the request. The guard converts that silent null into an explicit failure so the rest of setup does not dereference null.

Source

Thrown at packages/effects/src/pixel-dissolve.ts:247

	gl.linkProgram(program);
	if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
		const log = gl.getProgramInfoLog(program);
		gl.deleteProgram(program);
		throw new Error(`Pixel Dissolve program link failed: ${log ?? '(no log)'}`);
	}

	return program;
};

const createFullscreenQuad = (
	gl: WebGL2RenderingContext,
): {
	readonly vao: WebGLVertexArrayObject;
	readonly vbo: WebGLBuffer;
} => {
	const vao = gl.createVertexArray();
	if (!vao) {
		throw new Error('Failed to create WebGL vertex array');
	}

	gl.bindVertexArray(vao);

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

	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,
	);

	return {vao, vbo};
};

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Confirm WebGL2 actually initializes first: in a browser console run `const c=document.createElement('canvas'); const gl=c.getContext('webgl2'); console.log(gl && gl.createVertexArray());` — null/undefined means the environment, not your code.
  2. On Remotion Lambda ensure the function uses the published Chrome for Testing layer with GPU/SwiftShader enabled; do not strip the bundled Chromium.
  3. Reduce the number of simultaneously mounted WebGL2 effects in one composition — each pixelDissolve/pixelate/blur allocates its own context and VAO.
  4. If the context was lost mid-render, catch the WEBGL_lose_context event and remount the effect (re-run its setup) instead of reusing the stale state.
  5. Reproduce locally with `chrome --disable-gpu --use-gl=swiftshader` to confirm the failure is environment-driven.

Example fix

// before — assumes context always has capacity
import {pixelDissolve} from '@remotion/effects';
const effect = pixelDissolve(); // throws 'Failed to create WebGL vertex array' on starved GPU

// after — feature-detect before mounting the effect
function supportsWebGL2VAO(): boolean {
  try {
    const c = document.createElement('canvas');
    const gl = c.getContext('webgl2');
    return !!gl && !!gl.createVertexArray();
  } catch {
    return false;
  }
}
// branch on supportsWebGL2VAO() before applying pixelDissolve
Defensive patterns

Strategy: try-catch

Validate before calling

function webgl2Ready(): boolean {
  try {
    const c = document.createElement('canvas');
    const gl = c.getContext('webgl2');
    if (!gl || gl.isContextLost()) return false;
    const vao = gl.createVertexArray();
    if (vao) gl.deleteVertexArray(vao);
    return vao !== null;
  } catch {
    return false;
  }
}
// call webgl2Ready() before mounting pixelDissolve

Type guard

function isWebGL2(gl: WebGLRenderingContext | null): gl is WebGL2RenderingContext {
  return !!gl && typeof (gl as WebGL2RenderingContext).createVertexArray === 'function';
}

Try / catch

try {
  const effect = pixelDissolve();
} catch (err) {
  if ((err as Error).message === 'Failed to create WebGL vertex array') {
    // surface a friendly 'WebGL2 unavailable in this environment' message
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling pixelDissolve() and mounting it on a composition whose setup runs in an environment where the WebGL2 context is already lost or has exhausted vertex-array-object capacity. The exact line is the first allocation inside createFullscreenQuad, before any buffer or texture work.

Common situations: Headless rendering on Remotion Lambda/CI where Chrome falls back to SwiftShader; rendering many concurrent effect instances so VAO pool is drained; a previous frame triggered WEBGL_lose_context; running in a VM or RDP session with no real GPU; browser tab backgrounded long enough for the driver to reclaim the context.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/f1b0ea502755c4a5. Report an issue: GitHub.