gfx-rs/wgpu · critical

Could not create shader

Error message

Could not create shader

What it means

This panic fires in `create_srgb_present_program` when `gl.create_shader(glow::VERTEX_SHADER)` returns a null shader object while building the internal sRGB-present vertex shader on the WebGL/GLES backend. A null shader means the WebGL implementation refused to allocate a shader object, almost always due to context loss or exceeding the shader object limit. The panic aborts instance/device creation.

Source

Thrown at wgpu-hal/src/gles/web.rs:324

                    swapchain.extent.width as i32,
                    0,
                    0,
                    0,
                    swapchain.extent.width as i32,
                    swapchain.extent.height as i32,
                    glow::COLOR_BUFFER_BIT,
                    glow::NEAREST,
                )
            };
        }

        Ok(())
    }

    unsafe fn create_srgb_present_program(gl: &glow::Context) -> glow::Program {
        let program = unsafe { gl.create_program() }.expect("Could not create shader program");
        let vertex =
            unsafe { gl.create_shader(glow::VERTEX_SHADER) }.expect("Could not create shader");
        unsafe { gl.shader_source(vertex, include_str!("./shaders/srgb_present.vert")) };
        unsafe { gl.compile_shader(vertex) };
        let fragment =
            unsafe { gl.create_shader(glow::FRAGMENT_SHADER) }.expect("Could not create shader");
        unsafe { gl.shader_source(fragment, include_str!("./shaders/srgb_present.frag")) };
        unsafe { gl.compile_shader(fragment) };
        unsafe { gl.attach_shader(program, vertex) };
        unsafe { gl.attach_shader(program, fragment) };
        unsafe { gl.link_program(program) };
        unsafe { gl.delete_shader(vertex) };
        unsafe { gl.delete_shader(fragment) };
        unsafe { gl.bind_texture(glow::TEXTURE_2D, None) };

        program
    }

    pub fn supports_srgb(&self) -> bool {
        // present.frag takes care of handling srgb conversion

View on GitHub (pinned to 3e11ff59bf)

Solutions

  1. Listen for `webglcontextlost` (call `event.preventDefault()`) and fully re-create the wgpu Instance/Device/Surface after `webglcontextrestored`.
  2. Cut down context and shader object count: share one device across the app and destroy unused surfaces.
  3. Confirm the context is healthy before creating wgpu: `canvas.getContext('webgl2')` plus `gl.isContextLost()` check.
  4. Test on a different browser/driver to rule out GPU-driver-specific allocation failure; enable hardware acceleration.

Example fix

// before
let vertex = unsafe { gl.create_shader(glow::VERTEX_SHADER) }.expect("Could not create shader");
// after
let vertex = unsafe { gl.create_shader(glow::VERTEX_SHADER) }
    .ok_or(crate::CreateDeviceError::ResourceCreationFailed)?;
Defensive patterns

Strategy: try-catch

Validate before calling

const gl = canvas.getContext('webgl2');
if (!gl) throw new Error('WebGL2 unsupported');
if (gl.isContextLost()) throw new Error('WebGL2 context lost');
// sanity: shader object creation works
const probe = gl.createShader(gl.VERTEX_SHADER);
if (!probe) throw new Error('Cannot allocate shader objects');
gl.deleteShader(probe);

Type guard

function contextCanAllocateShaders(gl: WebGL2RenderingContext | null): gl is WebGL2RenderingContext {
  if (!gl || gl.isContextLost()) return false;
  const s = gl.createShader(gl.VERTEX_SHADER);
  if (!s) return false;
  gl.deleteShader(s);
  return true;
}

Try / catch

try {
  const adapter = await navigator.gpu.requestAdapter();
  const device = await adapter.requestDevice();
  device.lost.then(info => { if (info.reason !== 'destroyed') reinitWgpu(); });
} catch (e) {
  renderStaticFallback();
}

Prevention

When it happens

Trigger: wgpu instance/device initialization on the web backend, specifically the first `gl.create_shader(VERTEX_SHADER)` call inside `create_srgb_present_program`, when the WebGL context has been lost, the context is no longer current, or the driver/browser cannot allocate more shader objects.

Common situations: Pages that accumulate many contexts (each canvas/context has its own object budget); webglcontextlost fired by browser GPU-process reset; running inside iframes or headless browsers where WebGL fallback to SwiftShader runs out of resources; hidden-tab throttling leading to context loss between frames.

Related errors


AI-assisted analysis of gfx-rs/wgpu@3e11ff59bf (2026-09-03). Data as JSON: /api/errors/594dbde27132a265. Report an issue: GitHub.