remotion-dev/remotion · error · Error

Failed to create WebGL shader

Error message

Failed to create WebGL shader

What it means

Thrown inside compileShader() in fisheye-runtime.ts when gl.createShader(type) returns null (fisheye-runtime.ts:29-31). A null shader handle means the GL driver refused to allocate a shader object — context lost or shader-object pool exhausted. Without a handle, neither FISHEYE_VS nor FISHEYE_FS can be built, so fisheye setup aborts.

Source

Thrown at packages/effects/src/fisheye/fisheye-runtime.ts:30

	textureSource: WebGLTexture;
	uniforms: {
		uSource: WebGLUniformLocation | null;
		uCenter: WebGLUniformLocation | null;
		uFieldOfView: WebGLUniformLocation | null;
		uRadius: WebGLUniformLocation | null;
		uZoom: WebGLUniformLocation | null;
		uAspect: WebGLUniformLocation | null;
	};
};

const compileShader = (
	gl: WebGL2RenderingContext,
	type: number,
	source: string,
): WebGLShader => {
	const shader = gl.createShader(type);
	if (!shader) {
		throw new Error('Failed to create WebGL shader');
	}

	gl.shaderSource(shader, source);
	gl.compileShader(shader);
	if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
		const log = gl.getShaderInfoLog(shader);
		gl.deleteShader(shader);
		throw new Error(`Shader compile failed: ${log ?? '(no log)'}`);
	}

	return shader;
};

const linkProgram = (
	gl: WebGL2RenderingContext,
	vs: WebGLShader,
	fs: WebGLShader,
): WebGLProgram => {

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Render with the Angle backend (--gl=angle CLI / chromiumOptions.gl='angle' SSR / Angle in Studio).
  2. If using fisheye-runtime directly, always pair setupFisheye() with cleanupFisheye() so shaders/programs are freed.
  3. Reduce simultaneous WebGL2 effects and reuse identical fisheye() params so calculateKey reuses one setup.
  4. Check gl.isContextLost() and restore the context before retrying.
  5. Update GPU drivers or use a host with adequate GL resources.

Example fix

// before
const shader = gl.createShader(type);
if (!shader) {
  throw new Error('Failed to create WebGL shader');
}

// after (when using the runtime directly)
import {setupFisheye, cleanupFisheye} from '@remotion/effects/fisheye-runtime';
const state = setupFisheye(canvas);
try { /* applyFisheye(...) */ } finally { cleanupFisheye(state); }
Defensive patterns

Strategy: try-catch

Validate before calling

const canAllocateShader = (gl: WebGL2RenderingContext): boolean => {
  if (gl.isContextLost()) return false;
  const probe = gl.createShader(gl.VERTEX_SHADER);
  if (!probe) return false;
  gl.deleteShader(probe);
  return true;
};

Try / catch

import {setupFisheye, cleanupFisheye} from '@remotion/effects/fisheye-runtime';

let state;
try {
  state = setupFisheye(canvas);
} catch (err) {
  if (err instanceof Error && err.message === 'Failed to create WebGL shader') {
    // context lost or shader pool exhausted; retry on Angle after cleanup
    throw err;
  }
  throw err;
}
try { /* applyFisheye(state, ...) */ } finally { cleanupFisheye(state); }

Prevention

When it happens

Trigger: setupFisheye() -> createProgram() -> compileShader() runs while the WebGL2 context is lost (createShader must return null per spec) or after the driver's shader-object limit is reached from leaked/un-cleaned fisheye programs.

Common situations: Rendering on a host whose GPU process crashed leaving the context lost; long-running apps that call setupFisheye() directly without a matching cleanupFisheye(); CI with software GL and a low shader-object ceiling; many concurrent fisheye() instances.

Related errors


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