{"record":{"id":"8f1474a5139d81b4","repo":"perspective-dev/perspective","slug":"shader-link-error-name-info","errorCode":null,"errorMessage":"Shader link error [${name}]: ${info}","messagePattern":"Shader link error \\[(.+?)\\]: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/viewer-charts/src/ts/webgl/shader-registry.ts","lineNumber":79,"sourceCode":"        gl.compileShader(frag);\n        if (!gl.getShaderParameter(frag, gl.COMPILE_STATUS)) {\n            const info = gl.getShaderInfoLog(frag);\n            gl.deleteShader(vert);\n            gl.deleteShader(frag);\n            throw new Error(`Fragment shader compile error [${name}]: ${info}`);\n        }\n\n        program = gl.createProgram()!;\n        gl.attachShader(program, vert);\n        gl.attachShader(program, frag);\n        gl.linkProgram(program);\n\n        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n            const info = gl.getProgramInfoLog(program);\n            gl.deleteProgram(program);\n            gl.deleteShader(vert);\n            gl.deleteShader(frag);\n            throw new Error(`Shader link error [${name}]: ${info}`);\n        }\n\n        // Shaders can be deleted after linking\n        gl.deleteShader(vert);\n        gl.deleteShader(frag);\n\n        this._programs.set(name, program);\n        return program;\n    }\n\n    releaseAll(): void {\n        for (const program of this._programs.values()) {\n            this._gl.deleteProgram(program);\n        }\n\n        this._programs.clear();\n    }\n}","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/perspective-dev/perspective/blob/11c8238c0c9c0b9e490b5a6a2dc277afad1e980a/packages/viewer-charts/src/ts/webgl/shader-registry.ts#L61-L97","documentation":"After attaching compiled vertex and fragment shaders, getOrCreate calls gl.linkProgram and checks LINK_STATUS. Linking fails when the two shaders are incompatible (varying/uniform mismatches, missing main, too many uniforms/attributives for the GPU limits) even though each shader compiled individually. The registry deletes the program and both shaders before throwing, embedding the driver's program info log in the message.","triggerScenarios":"getOrCreate (or precompile) with vertex/fragment shader pairs whose varyings do not match in name/type/count, an inactive or over-limit uniform set, a fragment shader lacking main(), or a driver resource limit being exceeded during gl.linkProgram.","commonSituations":"Editing one shader of a pair and forgetting to update the matching varying declarations; renaming a varying in the vertex shader only; exceeding max fragment uniforms on low-end mobile GPUs; mixing a WebGL1-style vertex shader with a '#version 300 es' fragment shader.","solutions":["Read the ${info} log — link errors name the mismatched varying/uniform; align declarations between the vertex and fragment shaders (same name, type, and precision).","Check that all varyings written in the vertex shader are declared with identical type in the fragment shader (or vice versa).","Reduce uniform count if the log mentions limits; consolidate uniforms into a uniform block (WebGL2) or pack values into vectors.","Ensure both shaders use the same GLSL version and profile (both '#version 300 es' or both default).","If the pair is generated, add an assertion/test that links the pair offline (e.g. via a headless GL context) in CI."],"exampleFix":"// before: vertex shader emits a varying the fragment shader never declares\n// vert: varying vec3 vNormal; ... vNormal = n;\n// frag: (missing declaration)\n// after\n// vert: varying vec3 vNormal; ... vNormal = n;\n// frag: varying vec3 vNormal; ...","handlingStrategy":"try-catch","validationCode":"function validateShaderPair(vert: string, frag: string): string | null {\n  const vs = new Set([...vert.matchAll(/varying\\s+\\w+\\s+(\\w+)/g)].map(m => m[1]));\n  const fs = new Set([...frag.matchAll(/varying\\s+\\w+\\s+(\\w+)/g)].map(m => m[1]));\n  for (const v of vs) if (!fs.has(v)) return `varying \"${v}\" written in vertex shader but not declared in fragment shader`;\n  return null;\n}","typeGuard":"function isProgramLinked(gl: WebGL2RenderingContext, program: WebGLProgram): boolean {\n  return gl.getProgramParameter(program, gl.LINK_STATUS) === true;\n}","tryCatchPattern":"let program: WebGLProgram;\ntry {\n  program = registry.getOrCreate(name, vertSrc, fragSrc);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Shader link error\")) {\n    console.error(`Link failure for \"${name}\":`, e.message);\n    program = registry.getOrCreate(fallbackName, fallbackVert, fallbackFrag);\n  } else throw e;\n}","preventionTips":["Keep vertex/fragment shader pairs defined together in one module so varyings stay in sync.","Match varying names, types, and precision exactly across the pair.","Use the same GLSL version in both shaders.","Watch uniform counts against GPU limits (gl.MAX_FRAGMENT_UNIFORM_VECTORS) on mobile.","Add a CI smoke test that links every registered shader pair."],"tags":["webgl","shader-link","glsl","gpu"],"backgroundTag":"shader-link-error","analyzedSha":"11c8238c0c9c0b9e490b5a6a2dc277afad1e980a","analyzedAt":"2026-09-09T00:35:19.377Z","contentChangedAt":"2026-09-09T00:35:19.377Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}