remotion-dev/remotion · error · Error

Zigzag program link failed: ${log ?? '(no log)'}

Error message

Zigzag program link failed: ${log ?? '(no log)'}

What it means

After attaching and linking the zigzag shaders, the effect checks gl.LINK_STATUS; on failure it reads gl.getProgramInfoLog and throws 'Zigzag program link failed: <log>' (or '(no log)'). The shaders are fixed library constants that compiled, so a link failure typically indicates a driver linker bug, mismatched varying/attribute expectations on a non-conformant driver, or context loss during link — not user input.

Source

Thrown at packages/effects/src/zigzag.ts:379

};

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(`Zigzag program link failed: ${log ?? '(no log)'}`);
	}

	return program;
};

const createProgram = (
	gl: WebGL2RenderingContext,
	vertexSource: string,
	fragmentSource: string,
): WebGLProgram => {
	const vs = compileShader(gl, gl.VERTEX_SHADER, vertexSource);
	const fs = compileShader(gl, gl.FRAGMENT_SHADER, fragmentSource);
	const program = linkProgram(gl, vs, fs);
	gl.deleteShader(vs);
	gl.deleteShader(fs);
	return program;
};

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Inspect the embedded <log> for the driver's stated link error (e.g. unresolved varying) to confirm it is a driver issue.
  2. Render headless with Remotion's bundled Chromium and known-good SwiftShader; do not pass --disable-gpu.
  3. Update GPU drivers, especially on the failing host.
  4. Ensure context is not lost (isContextLost()) before rendering; re-mount on webglcontextlost.
  5. Lower concurrent WebGL effects to avoid memory pressure during link.
Defensive patterns

Strategy: try-catch

Validate before calling

// Program link outcome is driver-dependent on fixed library shaders; caller cannot prevent it.
// Pre-check context health and run a smoke-test link of trivial shaders.
const gl = document.createElement('canvas').getContext('webgl2');
const ok = gl != null && !gl.isContextLost();

Type guard

const canLinkTrivialProgram = (): boolean => {
  const gl = document.createElement('canvas').getContext('webgl2');
  if (!gl || gl.isContextLost()) return false;
  const mk = (t: number, src: string) => {
    const s = gl.createShader(t)!;
    gl.shaderSource(s, src);
    gl.compileShader(s);
    return s;
  };
  const vs = mk(gl.VERTEX_SHADER, '#version 300 es\nvoid main(){}');
  const fs = mk(gl.FRAGMENT_SHADER, '#version 300 es\nout highp vec4 o;void main(){o=vec4(0);}');
  const p = gl.createProgram()!;
  gl.attachShader(p, vs);
  gl.attachShader(p, fs);
  gl.linkProgram(p);
  const linked = gl.getProgramParameter(p, gl.LINK_STATUS) === true;
  gl.deleteProgram(p);
  return linked;
};

Try / catch

try {
  return <VideoEffects effects={[zigzag({colors: ['#ff0000', '#00ff00']})]} />;
} catch (err) {
  if (err instanceof Error && /Zigzag program link failed/.test(err.message)) {
    console.error(err.message); // contains driver link log
    return <Video />;
  }
  throw err;
}

Prevention

When it happens

Trigger: linkProgram in zigzag.ts: getProgramParameter(LINK_STATUS) is false; the thrown message embeds the driver link log or '(no log)'.

Common situations: Driver linker failing on valid GLSL-ES 3.00 programs; context lost mid-link; software renderer with incomplete program linking; memory exhaustion during link.

Related errors


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