perspective-dev/perspective · error · Error

Fragment shader compile error

Error message

Fragment shader compile error [${name}]: ${info}

What it means

The WebGL shader registry compiles a fragment shader via gl.compileShader and checks COMPILE_STATUS. If the GLSL source fed to the fragment shader is invalid (syntax error, unsupported construct, bad uniform/varying declaration), getOrCreate throws this Error including the shader name and the driver's info log. It deletes the partially-created vert/frag shader objects before throwing so no GPU resources leak.

Solutions

  1. Read the ${info} log in the message — the GLSL compiler reports the exact line and reason; fix the fragment shader source accordingly.
  2. Verify the fragment shader's #version directive matches the context (WebGL1: GLSL ES 1.00 default, WebGL2: '#version 300 es' first line, no leading whitespace/newline).
  3. Add required precision qualifiers (e.g. 'precision mediump float;') which are mandatory in fragment shaders on mobile GPUs.
  4. Test on the failing device/driver — some constructs compile on desktop but fail on ANGLE/Adreno/Mali; keep a fallback shader path.
  5. If shaders are templated/generated, log the fully-assembled fragSrc before compiling to spot concatenation mistakes.

Example fix

// before
const fragSrc = `
  varying vec3 color;
  void main() { gl_FragColor = vec4(color, 1.0); }
`;
// after (add mandatory precision for mobile GLSL ES)
const fragSrc = `
  precision mediump float;
  varying vec3 color;
  void main() { gl_FragColor = vec4(color, 1.0); }
`;
Defensive patterns

Strategy: try-catch

Validate before calling

function validateFragSrc(src: string): string | null {
  if (!/main\s*\(/.test(src)) return "missing main()";
  if (/#version 300 es/.test(src) && !src.trimStart().startsWith("#version")) return "#version must be first line";
  if (!/#version 300 es/.test(src) && !/precision\s+(lowp|mediump|highp)\s+float/.test(src)) return "missing float precision qualifier";
  return null;
}

Type guard

function isShaderCompiled(gl: WebGL2RenderingContext, shader: WebGLShader): boolean {
  return gl.getShaderParameter(shader, gl.COMPILE_STATUS) === true;
}

Try / catch

try {
  const program = registry.getOrCreate(name, vertSrc, fragSrc);
} catch (e) {
  if (e instanceof Error && e.message.startsWith("Fragment shader compile error")) {
    console.error(`Bad fragment shader "${name}":`, e.message);
    fallBackToFlatColorProgram();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling getOrCreate (directly or via precompile) with a fragment shader source that fails gl.getShaderParameter(frag, gl.COMPILE_STATUS), e.g. GLSL syntax errors, using an unsupported GLSL version directive, or referencing undeclared variables/texture units.

Common situations: Hand-edited shader templates with a typo; shaders built by string concatenation that produce invalid GLSL on some GPU/driver (works on desktop GL, fails on mobile ES); using WebGL2-only syntax (e.g. 'in'/'out', texture()) in a WebGL1 context; missing precision qualifiers for float types on mobile.

Related errors


AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09). Data as JSON: /api/errors/5ddc4ea1b9d638c4. Report an issue: GitHub.

Appendix: source

Thrown at packages/viewer-charts/src/ts/webgl/shader-registry.ts:66

        const gl = this._gl;

        const vert = gl.createShader(gl.VERTEX_SHADER)!;
        gl.shaderSource(vert, vertSrc);
        gl.compileShader(vert);
        if (!gl.getShaderParameter(vert, gl.COMPILE_STATUS)) {
            const info = gl.getShaderInfoLog(vert);
            gl.deleteShader(vert);
            throw new Error(`Vertex shader compile error [${name}]: ${info}`);
        }

        const frag = gl.createShader(gl.FRAGMENT_SHADER)!;
        gl.shaderSource(frag, fragSrc);
        gl.compileShader(frag);
        if (!gl.getShaderParameter(frag, gl.COMPILE_STATUS)) {
            const info = gl.getShaderInfoLog(frag);
            gl.deleteShader(vert);
            gl.deleteShader(frag);
            throw new Error(`Fragment shader compile error [${name}]: ${info}`);
        }

        program = gl.createProgram()!;
        gl.attachShader(program, vert);
        gl.attachShader(program, frag);
        gl.linkProgram(program);

        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            const info = gl.getProgramInfoLog(program);
            gl.deleteProgram(program);
            gl.deleteShader(vert);
            gl.deleteShader(frag);
            throw new Error(`Shader link error [${name}]: ${info}`);
        }

        // Shaders can be deleted after linking
        gl.deleteShader(vert);
        gl.deleteShader(frag);

View on GitHub (pinned to 11c8238c0c)