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
- On web: verify the canvas element exists and the browser supports WebGL2/WebGPU; test with an up-to-date browser
- Ensure all windows are created on the main thread (default winit behavior — avoid custom thread-based window creation)
- Try a different backend via WGPU_BACKEND or update GPU drivers if the failure is native
- 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
- Create all windows on the main thread (default winit path) — surface creation is main-thread-only on some OSes
- On web, ensure the target canvas exists and the browser supports WebGL2/WebGPU before app startup
- Wrap risky window setups in a configuration that can fall back to an existing surface or headless mode
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
- Failed to create wgpu surface
- Please use a more specific shader stage: https://github.com/
- Failed to poll device for map async
- Failed to build bind group: {0}
- Failed to build event loop
AI-assisted analysis of bevyengine/bevy@8d743eb7dc (2026-08-20).
Data as JSON: /api/errors/f9c5d0fda57e6669.
Report an issue: GitHub.