phaserjs/phaser · error · Error

Link Shader failed:

Error message

Link Shader failed:

What it means

Thrown by WebGLProgramWrapper when both vertex and fragment shaders compiled successfully but gl.LINK_STATUS is still false. This means the shaders are individually valid but incompatible with each other (e.g. varying/in-out mismatch, missing attribute bindings, or unsupported feature combination). Both shader sources are console.logged before the throw.

Source

Thrown at src/renderer/webgl/wrappers/WebGLProgramWrapper.js:345

        var fs = this._fragmentShader;

        var failed = 'Shader failed:\n';

        if (!gl.getProgramParameter(program, gl.LINK_STATUS))
        {
            if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS))
            {
                console.log(this.vertexSource);
                throw new Error('Vertex ' + failed + gl.getShaderInfoLog(vs));
            }

            if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS))
            {
                console.log(this.fragmentSource);
                throw new Error('Fragment ' + failed + gl.getShaderInfoLog(fs));
            }
            console.log(this.vertexSource, this.fragmentSource);
            throw new Error('Link Shader failed:' + gl.getProgramInfoLog(program));
        }

        this._setupAttributesAndUniforms();

        this.compileTimeMs = performance.now() - this._compileStartTime;
        this.compiling = false;
    },

    /**
     * Set up the attributes and uniforms for this program.
     * This is called after the program is created or re-created.
     *
     * @method Phaser.Renderer.WebGL.Wrappers.WebGLProgramWrapper#_setupAttributesAndUniforms
     * @private
     * @since 4.0.0
     */
    _setupAttributesAndUniforms: function ()
    {

View on GitHub (pinned to 41be1e462b)

Solutions

  1. Read gl.getProgramInfoLog output appended to the error; link errors usually name the mismatched varying.
  2. Ensure vertex 'out' declarations and fragment 'in' declarations match in name, type, and (for WebGL2) layout location.
  3. Use a consistent #version in both stages.
  4. Confirm shaderAdditions applied to one stage have a matching patch in the other where varyings cross the boundary.
  5. Compare the two console.logged sources side by side for the interface block.

Example fix

// before: vertex outputs vUv but fragment declares inTuv
// vertex: out vec2 vUv;
// fragment: in vec2 inTuv; // name mismatch -> link fails

// after
// fragment: in vec2 vUv;
Defensive patterns

Strategy: try-catch

Validate before calling

function interfacesMatch(vsOuts, fsIns) {
  const v = new Set(vsOuts.map(o => o.name + ':' + o.type));
  return fsIns.every(i => v.has(i.name + ':' + i.type));
}

Try / catch

try { program.create(vs, fs); } catch (e) { if (/Link Shader failed/.test(e.message)) { console.error('Link error:', e.message); /* align varyings between stages */ } else throw e; }

Prevention

When it happens

Trigger: Vertex shader outputs (varying/out) that the fragment shader does not consume or declares with a different type; fragment shader inputs the vertex shader never writes; missing/inconsistent layout(location=...) bindings; exceeding varying limits; using transform-feedback-only varyings incorrectly.

Common situations: Mixing GLSL versions between the two shaders (vertex 1.0, fragment 3.0); editing one shader but not the other during development; renaming a varying in one file only; shaderAdditions that patch one stage but not the matching one.

Related errors


AI-assisted analysis of phaserjs/phaser@41be1e462b (2026-08-13). Data as JSON: /api/errors/4973fc276d4be4ed. Report an issue: GitHub.