Comfy-Org/ComfyUI · error · RuntimeError

Program linking failed:\n{error}

Error message

Program linking failed:\n{error}

What it means

Raised by _create_program() when glLinkProgram() leaves GL_LINK_STATUS false; the driver's linker log is embedded in the message. Compilation of both stages succeeded, but the vertex and fragment shaders do not fit together — the classic causes are a varying/out declared in one stage but missing or mismatched type in the other, or more active samplers/uniforms than the ES3 limit.

Source

Thrown at comfy_extras/nodes_glsl.py:381

        fragment_shader = _compile_shader(fragment_source, gl.GL_FRAGMENT_SHADER)
    except RuntimeError:
        gl.glDeleteShader(vertex_shader)
        raise

    program = gl.glCreateProgram()
    gl.glAttachShader(program, vertex_shader)
    gl.glAttachShader(program, fragment_shader)
    gl.glLinkProgram(program)

    gl.glDeleteShader(vertex_shader)
    gl.glDeleteShader(fragment_shader)

    if not gl.glGetProgramiv(program, gl.GL_LINK_STATUS):
        error = gl.glGetProgramInfoLog(program)
        if isinstance(error, bytes):
            error = error.decode(errors="replace")
        gl.glDeleteProgram(program)
        raise RuntimeError(f"Program linking failed:\n{error}")

    return program


def _render_shader_batch(
    fragment_code: str,
    width: int,
    height: int,
    image_batches: list[list[np.ndarray]],
    floats: list[float],
    ints: list[int],
    bools: list[bool] | None = None,
    curves: list[np.ndarray] | None = None,
) -> list[list[np.ndarray]]:
    """
    Render a fragment shader for multiple batches efficiently.

    Compiles shader once, reuses framebuffer/textures across batches.

View on GitHub (pinned to 1c6d8d45b3)

Solutions

  1. Read the linker info log in the error — it states exactly which symbol failed to match.
  2. Keep the fragment shader's 'in' declarations identical (name + type) to the vertex stage's 'out' declarations.
  3. Count sampler uniforms and stay under the implementation limit (query GL_MAX_TEXTURE_IMAGE_UNITS; commonly 16).
  4. Simplify the shader to a minimal version and add pieces back until the failing construct is found.

Example fix

// before (vertex out: vec2 v_uv)
// fragment: in vec2 uv; ...   // name mismatch -> link error

// after
// fragment: in vec2 v_uv; ... // matches vertex out exactly
Defensive patterns

Strategy: try-catch

Validate before calling

# If your fragment shader declares inputs, verify they match the vertex stage outputs
custom_ins = set(re.findall(r"^\s*in\s+\w+\s+(\w+)", fragment_code, re.M))
expected = {"v_uv"}  # names emitted by the node's fixed vertex shader (adjust per node)
assert custom_ins <= expected or custom_ins == expected, f"unmatched fragment inputs: {custom_ins - expected}"

Try / catch

try:
    out = run_glsl(fragment_code, ...)
except RuntimeError as e:
    if "Program linking failed" in str(e):
        # the log lists the mismatched symbol; fix 'in' names/types to match vertex 'out's
        raise ValueError(f"Fragment inputs must match vertex outputs: {e}") from e
    raise

Prevention

When it happens

Trigger: Editing the fragment shader's inputs so they no longer match the fixed vertex shader's outputs (name, type, or qualifier mismatch); exceeding GL_MAX_TEXTURE_IMAGE_UNITS by sampling too many textures; declaring conflicting precision for the same interpolated variable.

Common situations: Customizing the fragment code of the GLSL node and renaming/removing the interpolated varyings the built-in vertex stage emits; heavy multi-texture shaders on mobile/software renderers with low sampler limits.

Related errors


AI-assisted analysis of Comfy-Org/ComfyUI@1c6d8d45b3 (2026-08-14). Data as JSON: /api/errors/11baef3c023a4a7b. Report an issue: GitHub.