remotion-dev/remotion · error · Error

Failed to create shadows and highlights vertex array

Error message

Failed to create shadows and highlights vertex array

What it means

The shadowsHighlights() effect calls gl.createVertexArray() during its setup phase and throws this Error when the WebGL2 context returns null. A null VAO means the GPU driver refused to allocate a vertex array object even though the context itself was successfully acquired. This is an environment-level failure, not a parameter validation error.

Source

Thrown at packages/effects/src/shadows-highlights.ts:206

const setupShadowsHighlights = (
	target: HTMLCanvasElement,
): ShadowsHighlightsState => {
	const gl = target.getContext('webgl2', {
		premultipliedAlpha: true,
		alpha: true,
		preserveDrawingBuffer: true,
	});
	if (!gl) {
		throw createWebGL2ContextError('shadows and highlights 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 shadows and highlights vertex array');
	}

	const vbo = gl.createBuffer();
	if (!vbo) {
		throw new Error('Failed to create shadows and highlights 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,
	);

	const aPos = gl.getAttribLocation(program, 'aPos');
	const aUv = gl.getAttribLocation(program, 'aUv');
	gl.enableVertexAttribArray(aPos);

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Ensure your render environment has GPU acceleration enabled — for headless Chrome pass flags like --use-gl=angle --use-angle=gl --enable-features=Vulkan or run on a runner with a discrete GPU.
  2. Reduce the number of distinct WebGL2-backed effects applied simultaneously; each shadowsHighlights() instance allocates its own context and resources.
  3. Check for WebGL2 context loss by listening to the 'webglcontextlost' event on the canvas before relying on the effect.
  4. If running in Lambda, verify the Chrome binary and layer include GPU-compatible libraries (libGL, libEGL, libGLESv2).

Example fix

// before — fails on a GPU-less CI runner
renderMedia({
  composition,
  serveUrl,
  inputProps,
});

// after — enable GPU in the headless Chromium used by the renderer
// (set via puppeteerInstance or renderMedia chromiumOptions)
renderMedia({
  composition,
  serveUrl,
  inputProps,
  chromiumOptions: {
    enableMultiProcessOnLinux: true,
    // ensure the headless shell picks up system GL
  },
});
// and launch Chrome with: --use-gl=angle --use-angle=gl
Defensive patterns

Strategy: try-catch

Validate before calling

// Before applying shadowsHighlights, verify the canvas can get a WebGL2 context
function canAllocateWebGL2(canvas: HTMLCanvasElement): boolean {
  const gl = canvas.getContext('webgl2', { alpha: true });
  if (!gl) return false;
  const testVao = gl.createVertexArray();
  const ok = testVao !== null;
  if (testVao) gl.deleteVertexArray(testVao);
  // Note: getContext returns the same context on repeated calls; do not lose it
  return ok;
}

Type guard

null

Try / catch

try {
  // Apply the effect in a composition
  shadowsHighlights({ shadows: -0.5 });
} catch (e) {
  if (e instanceof Error && e.message.includes('vertex array')) {
    // WebGL2 resource allocation failed — fall back to no effect or a 2D-canvas approach
    console.warn('WebGL2 VAO unavailable, skipping shadowsHighlights');
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling shadowsHighlights() in a composition that renders on a machine whose GPU or driver cannot service WebGL2 VAO allocation. Most commonly seen in headless Chromium without GPU passthrough, in CI runners with software rendering, or when too many simultaneous WebGL2 contexts have exhausted the driver's object pool.

Common situations: Running Remotion Lambda or Chrome Headless Shell in CI without --enable-features=Vulkan or proper GPU flags; rendering on a VM with no GPU; opening dozens of effects-heavy compositions in parallel tabs that each create their own WebGL2 context; a transient context-loss event immediately after getContext succeeded.

Related errors


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