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
- Read the ${info} log in the message — the GLSL compiler reports the exact line and reason; fix the fragment shader source accordingly.
- 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).
- Add required precision qualifiers (e.g. 'precision mediump float;') which are mandatory in fragment shaders on mobile GPUs.
- Test on the failing device/driver — some constructs compile on desktop but fail on ANGLE/Adreno/Mali; keep a fallback shader path.
- 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
- Always start fragment shaders with a precision qualifier for float.
- Keep the #version directive on the very first line with no leading whitespace.
- Lint/validate generated shader sources in unit tests with a headless GL context.
- Test shaders on the lowest-target device (mobile GLES) before shipping.
- Log the fully-assembled source when shaders are templated.
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)