remotion-dev/remotion · error · Error
Failed to create WebGL program
Error message
Failed to create WebGL program
What it means
Thrown inside linkProgram() in fisheye-runtime.ts when gl.createProgram() returns null (fisheye-runtime.ts:49-52). A null program object means the GL driver would not allocate a program handle — context lost or program-object pool exhausted. Both fisheye shaders have compiled, but there is no program to link them into.
Source
Thrown at packages/effects/src/fisheye/fisheye-runtime.ts:51
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 => {
const program = gl.createProgram();
if (!program) {
throw new Error('Failed to create WebGL program');
}
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
const log = gl.getProgramInfoLog(program);
gl.deleteProgram(program);
throw new Error(`Program link failed: ${log ?? '(no log)'}`);
}
return program;
};
const createProgram = (
gl: WebGL2RenderingContext,
vertexSource: string,
fragmentSource: string,View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Render with the Angle backend (--gl=angle / chromiumOptions.gl='angle' / Angle in Studio).
- If using fisheye-runtime directly, always pair setupFisheye() with cleanupFisheye() so gl.deleteProgram frees the handle.
- Reduce simultaneous WebGL2 effects and reuse identical fisheye() params via calculateKey.
- Verify gl.isContextLost() is false and restore the context otherwise.
- Update GPU drivers or move to a host with adequate GL resources.
Example fix
// before
const program = gl.createProgram();
if (!program) {
throw new Error('Failed to create WebGL program');
}
// after (ensure cleanup when using the runtime directly)
const state = setupFisheye(canvas);
try { /* applyFisheye(...) */ } finally { cleanupFisheye(state); } Defensive patterns
Strategy: try-catch
Validate before calling
const canAllocateProgram = (gl: WebGL2RenderingContext): boolean => {
if (gl.isContextLost()) return false;
const probe = gl.createProgram();
if (!probe) return false;
gl.deleteProgram(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 program') {
// free other fisheye states, retry on Angle
throw err;
}
throw err;
}
try { /* applyFisheye(state, ...) */ } finally { cleanupFisheye(state); } Prevention
- When using fisheye-runtime directly, always pair setupFisheye() with cleanupFisheye() (gl.deleteProgram).
- Render with Angle (--gl=angle / chromiumOptions.gl='angle').
- Limit simultaneous WebGL2 effects; reuse identical fisheye() params via calculateKey.
- Handle webglcontextlost and restore before retrying.
When it happens
Trigger: setupFisheye() -> createProgram() -> linkProgram() reaches gl.createProgram() and gets null. This occurs when the WebGL2 context is lost or when leaked programs (from setupFisheye() without cleanupFisheye(), or many effect() instances) have exhausted the driver's program-object budget.
Common situations: Apps using fisheye-runtime directly without cleanupFisheye(); long-running Studio leaking programs; compositions with many simultaneous fisheye()/WebGL2 effects; CI on hosts whose GPU process crashed.
Related errors
- Failed to create exposure shader program
- Failed to create WebGL shader
- Failed to create WebGL texture
- Failed to create WebGL vertex array
- Failed to create WebGL buffer
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/0f6fbe37205a2d72.
Report an issue: GitHub.