bevyengine/bevy · critical

Failed to build event loop

Error message

Failed to build event loop

What it means

During startup bevy_winit builds the platform event loop with winit's EventLoopBuilder and .expect("Failed to build event loop") — any winit failure here aborts the process before the app runs. winit fails when it cannot connect to a display server (X11/Wayland), platform libraries are missing, or platform integration is misconfigured (e.g. Android without the app handle set up by #[bevy_main]).

Source

Thrown at crates/bevy_winit/src/lib.rs:137

        }

        #[cfg(target_os = "windows")]
        {
            use winit::platform::windows::EventLoopBuilderExtWindows;
            event_loop_builder.with_any_thread(self.run_on_any_thread);
        }

        #[cfg(target_os = "android")]
        {
            use winit::platform::android::EventLoopBuilderExtAndroid;
            let msg = "Bevy must be setup with the #[bevy_main] macro on Android";
            event_loop_builder
                .with_android_app(bevy_android::ANDROID_APP.get().expect(msg).clone());
        }

        let event_loop = event_loop_builder
            .build()
            .expect("Failed to build event loop");

        let event_loop_proxy = event_loop.create_proxy();

        // Wake up the event loop when `Ctrl+C` is received so that the app can
        // exit even while idle in a reactive update mode
        #[cfg(any(all(unix, not(target_os = "horizon")), windows))]
        {
            let event_loop_proxy = event_loop_proxy.clone();
            bevy_app::TerminalCtrlCHandlerPlugin::register_exit_handler(move || {
                let _ = event_loop_proxy.send_event(WinitUserEvent::WakeUp);
            });
        }

        app.init_resource::<WinitMonitors>()
            .init_resource::<WinitSettings>()
            .insert_resource(DisplayHandleWrapper(event_loop.owned_display_handle()))
            .insert_resource(EventLoopProxyWrapper(event_loop_proxy))
            .add_message::<RawWinitWindowEvent>()

View on GitHub (pinned to 396ca72708)

Solutions

  1. Run inside a graphical session, or forward one over SSH (ssh -X) / WSLg on Windows.
  2. On headless CI, use a virtual framebuffer: xvfb-run ./your_app, or install the X11/Wayland client libraries (libxkbcommon, libx11, libwayland) if they are missing.
  3. For genuinely headless workloads, build without the windowing plugins or configure WindowPlugin with primary_window: None and use a headless renderer setup.
  4. On Android, enter via #[bevy_main] so the android app handle is available to the event loop builder.
  5. Check backend environment variables (e.g. WGPU_BACKEND, WINIT_UNIX_BACKEND) for values unsupported on the machine.

Example fix

# before (headless server / CI)
./my_bevy_game   # panics: Failed to build event loop

# after
xvfb-run -a ./my_bevy_game   # virtual display for windowed apps on headless machines
Defensive patterns

Strategy: validation

Validate before calling

#[cfg(target_os = "linux")]
fn display_available() -> bool {
    std::env::var("DISPLAY").is_ok() || std::env::var("WAYLAND_DISPLAY").is_ok()
}

#[cfg(target_os = "linux")]
fn main() {
    if !display_available() {
        eprintln!("no display server found; set DISPLAY/WAYLAND_DISPLAY or run under xvfb-run");
        std::process::exit(1);
    }
    my_game::run();
}

Try / catch

// Startup abort, not a Result: validate the environment before App::new().run().
// In CI, choose headless plugins or wrap execution with `xvfb-run` instead of catching.

Prevention

When it happens

Trigger: Running a windowed Bevy app on a headless Linux machine or CI runner with no $DISPLAY/WAYLAND_DISPLAY; SSH sessions without X forwarding; missing libxkbcommon/X11/Wayland client libraries; Android builds not using #[bevy_main]; sandboxed environments denying display sockets.

Common situations: CI pipelines launching integration tests that accidentally include DefaultPlugins; deploying a game binary to a server; minimal container images without graphics runtime libs; fresh Linux installs without mesa/X11; WSL without WSLg.

Related errors


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