bevyengine/bevy · critical
Failed to create wgpu surface
Error message
Failed to create wgpu surface
What it means
During RenderPlugin initialization Bevy creates the wgpu Surface for the primary window from its raw handle (via instance.create_surface) and expects success. Surface creation fails when the chosen wgpu instance/backends cannot present to that window — missing GPU drivers, a backend/display-server mismatch, or (on web) a canvas without a WebGPU/WebGL2 context.
Source
Thrown at crates/bevy_render/src/renderer/mod.rs:253
#[cfg(feature = "raw_vulkan_init")]
let instance = raw_vulkan_init::create_raw_vulkan_instance(
instance_descriptor,
&raw_vulkan_init_settings,
&mut additional_vulkan_features,
);
let surface = primary_window.and_then(|wrapper| {
let maybe_handle = wrapper
.0
.lock()
.expect("Couldn't get the window handle in time for renderer initialization");
if let Some(wrapper) = maybe_handle.as_ref() {
// SAFETY: Plugins should be set up on the main thread.
let handle = unsafe { wrapper.get_handle() };
Some(
instance
.create_surface(handle)
.expect("Failed to create wgpu surface"),
)
} else {
None
}
});
let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK_ADAPTER")
.map_or(options.force_fallback_adapter, |v| {
!(v.is_empty() || v == "0" || v == "false")
});
let desired_adapter_name = std::env::var("WGPU_ADAPTER_NAME")
.as_deref()
.map_or(options.adapter_name.clone(), |x| Some(x.to_lowercase()));
let request_adapter_options = RequestAdapterOptions {
power_preference: options.power_preference,
compatible_surface: surface.as_ref(),View on GitHub (pinned to 396ca72708)
Solutions
- Install/verify GPU drivers and the required libraries (Vulkan loader, libx11/libwayland, EGL/GLX)
- Set WGPU_BACKEND to a backend the machine actually supports (e.g. WGPU_BACKEND=gl on X11-less setups)
- For headless rendering/CI/tests, run without a primary window: WindowPlugin { primary_window: None, ..default() } with Bevy's headless rendering support instead of a real surface
- On web, confirm the canvas element exists and the browser supports WebGL2 or WebGPU
Example fix
// before (needs a real surface)
App::new().add_plugins(DefaultPlugins);
// after (headless / CI)
App::new().add_plugins(
DefaultPlugins.set(WindowPlugin {
primary_window: None,
exit_condition: ExitCondition::DontExit,
..default()
}),
); Defensive patterns
Strategy: fallback
Validate before calling
// Before building the app, decide whether a real surface is possible:
let headless = std::env::var("CI").is_ok() || std::env::var("DISPLAY").is_err() && cfg!(target_os = "linux");
// then choose WindowPlugin { primary_window: if headless { None } else { Some(Default::default()) }, ..default() } Prevention
- Keep GPU drivers and Vulkan/GL system libraries installed on dev machines
- Use headless rendering (primary_window: None) for CI and tests
- Set WGPU_BACKEND explicitly when the default backend selection is wrong for the machine
When it happens
Trigger: App::new() with a default primary window on a machine where the enabled wgpu backends cannot create a surface for it: no Vulkan/GL drivers, SSH session without X forwarding, Wayland/X11 mismatch, WGPU_BACKEND forcing an unsupported backend, or a browser canvas that fails context acquisition.
Common situations: Running on bare CI containers or remote servers without a display; forced WGPU_BACKEND=gl on a system without GLX/EGL; outdated GPU drivers on Windows/Linux; web builds targeting browsers without WebGL2.
Related errors
- Failed to create wgpu surface
- No supported formats for surface
- Please use a more specific shader stage: https://github.com/
- Failed to poll device for map async
- Failed to build bind group: {0}
AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20).
Data as JSON: /api/errors/fdfd625f5ea7d037.
Report an issue: GitHub.