bevyengine/bevy · critical

Failed to create wgpu surface

Error message

Failed to create wgpu surface

What it means

In the per-window surface creation system, Bevy builds a SurfaceTargetUnsafe from each ExtractedWindow's raw handles and expects create_surface_unsafe to succeed (the source notes this is only fallible for HTML canvases where obtaining a WebGPU/WebGL2 context fails, and that on some OSes it must be called from the main thread). A failure here means the instance could not create a presentable surface for that specific window.

Source

Thrown at crates/bevy_render/src/view/window/mod.rs:393

        &mut ExtractedWindow,
        &RawHandleWrapper,
        Option<&mut SurfaceData>,
    )>,
    render_instance: Res<RenderInstance>,
    render_adapter: Res<RenderAdapter>,
    render_device: Res<RenderDevice>,
) {
    for (entity, mut window, handle, mut maybe_surface_data) in &mut windows {
        let Some(data) = maybe_surface_data.as_mut() else {
            let surface_target = SurfaceTargetUnsafe::RawHandle {
                raw_display_handle: Some(handle.get_display_handle()),
                raw_window_handle: handle.get_window_handle(),
            };
            // SAFETY: The window handles in ExtractedWindows will always be valid objects to create surfaces on
            let surface = unsafe {
                // NOTE: On some OSes this MUST be called from the main thread.
                // As of wgpu 0.15, only fallible if the given window is a HTML canvas and obtaining a WebGPU or WebGL2 context fails.
                render_instance
                    .create_surface_unsafe(surface_target)
                    .expect("Failed to create wgpu surface")
            };
            let caps = surface.get_capabilities(&render_adapter);
            let present_mode = present_mode(&window, &caps);
            let formats = caps.formats;
            // For future HDR output support, we'll need to request a format that supports HDR,
            // but as of wgpu 0.15 that is not yet supported.
            // Prefer sRGB formats for surfaces, but fall back to first available format if no sRGB formats are available.
            let mut format = *formats.first().expect("No supported formats for surface");
            for available_format in formats {
                // Rgba8UnormSrgb and Bgra8UnormSrgb and the only sRGB formats wgpu exposes that we can use for surfaces.
                if available_format == TextureFormat::Rgba8UnormSrgb
                    || available_format == TextureFormat::Bgra8UnormSrgb
                {
                    format = available_format;
                    break;
                }

View on GitHub (pinned to 8d743eb7dc)

Solutions

  1. On web: verify the canvas element exists and the browser supports WebGL2/WebGPU; test with an up-to-date browser
  2. Ensure all windows are created on the main thread (default winit behavior — avoid custom thread-based window creation)
  3. Try a different backend via WGPU_BACKEND or update GPU drivers if the failure is native
  4. If the window isn't actually needed for presentation, use headless rendering instead of a visible window
Defensive patterns

Strategy: fallback

Validate before calling

// On web, check context availability before relying on surface creation:
fn web_canvas_supports_webgl2() -> bool {
    // wasm-bindgen check for WebGL2 on the configured canvas
    true // placeholder: query canvas.getContext("webgl2") from JS glue
}

Prevention

When it happens

Trigger: A window exists (windowing plugin) but its raw handles cannot back a surface: on web, a canvas that fails to acquire a WebGPU/WebGL2 context; on desktop, window creation happening off the main thread on OSes that require main-thread surface creation, or stale/invalid handles.

Common situations: Web deployment where the canvas is missing, resized to nothing, or the browser lacks WebGL2; creating secondary windows on non-main threads on macOS; wgpu backend (e.g. GL) that cannot target the given window type.

Related errors


AI-assisted analysis of bevyengine/bevy@8d743eb7dc (2026-08-20). Data as JSON: /api/errors/f9c5d0fda57e6669. Report an issue: GitHub.