iced-rs/iced · critical
Create event loop
Error message
Create event loop
What it means
This panic occurs when winit's EventLoop::with_user_event().build() returns an error inside iced_winit::run (winit/src/lib.rs:81-83), before any window exists. EventLoopError means the platform could not initialize its event loop: on Linux there is no connection to a display server (no X11 via DISPLAY and no Wayland via WAYLAND_DISPLAY), and on any platform creating a second event loop in the same process fails (RecreationAttempt). Because iced unwraps with .expect("Create event loop"), the application dies at startup with this message.
Source
Thrown at winit/src/lib.rs:83
use std::mem::ManuallyDrop;
use std::slice;
use std::sync::Arc;
/// Runs a [`Program`] with the provided settings.
pub fn run<P>(program: P) -> Result<(), Error>
where
P: Program + 'static,
P::Theme: theme::Base,
{
use winit::event_loop::EventLoop;
let boot_span = debug::boot();
let settings = program.settings();
let window_settings = program.window();
let event_loop = EventLoop::with_user_event()
.build()
.expect("Create event loop");
let backend_settings = backend::Settings::from(&settings);
let renderer_settings = renderer::Settings::from(&settings);
let display_handle = event_loop.owned_display_handle();
let (proxy, worker) = Proxy::new(event_loop.create_proxy());
#[cfg(feature = "debug")]
{
let proxy = proxy.clone();
debug::on_hotpatch(move || {
proxy.send_action(Action::Reload);
});
}
let mut runtime = {
let executor = P::Executor::new().map_err(Error::ExecutorCreationFailed)?;View on GitHub (pinned to 2cffa99b39)
Solutions
- Provide a display: in CI/Docker run under `xvfb-run -a cargo test` / `xvfb-run -a ./app`; over SSH use `ssh -X` or `ssh -Y`, or set DISPLAY to a reachable X server.
- On Wayland ensure WAYLAND_DISPLAY points at a live socket (check `ls $XDG_RUNTIME_DIR/$WAYLAND_DISPLAY`); on X11 verify with `echo $DISPLAY` and `xdpyinfo`.
- Never create the event loop twice in one process: call iced::run exactly once from main(); in integration tests run one app per test binary, spawn each case as a child process, or test UI logic with iced's headless runtime APIs instead of the full shell.
- If the failure is a missing library rather than a missing display, install the X11 stack (libxcb, libxkbcommon, libxkbcommon-x11) or build iced with only the `wayland` feature to drop the X11 dependency entirely.
- If you need no window at all (render to an image, offscreen compositing), use wgpu headlessly or iced's headless rendering support instead of iced_winit::run.
Example fix
// before — panics with "Create event loop" in headless CI/containers
fn main() -> iced::Result {
iced::run("Todo", Todos::update, Todos::view)
}
// after — fail fast with an actionable message instead of a winit panic
fn main() -> iced::Result {
#[cfg(target_os = "linux")]
if std::env::var("DISPLAY").is_err() && std::env::var("WAYLAND_DISPLAY").is_err() {
eprintln!("no display server found: set DISPLAY/WAYLAND_DISPLAY or run under xvfb-run");
std::process::exit(64);
}
iced::run("Todo", Todos::update, Todos::view)
} Defensive patterns
Strategy: validation
Validate before calling
// Run before calling iced::run / any winit API on Linux
fn display_available() -> bool {
std::env::var("DISPLAY").is_ok() || std::env::var("WAYLAND_DISPLAY").is_ok()
}
fn main() -> iced::Result {
#[cfg(target_os = "linux")]
if !display_available() {
eprintln!("no display server: set DISPLAY/WAYLAND_DISPLAY or run under xvfb-run");
std::process::exit(64);
}
iced::run("App", Update::new, View::new)
}
# CI equivalent: xvfb-run -a cargo test -- --nocapture Prevention
- Call iced::run (and any EventLoop construction) exactly once per process, from the main thread.
- Fail fast at startup: check DISPLAY/WAYLAND_DISPLAY on Linux and print a remediation hint instead of letting winit panic.
- In Dockerfiles and CI, install xvfb and launch via xvfb-run; document the display requirement in the README.
- Test UI logic with iced's runtime/headless APIs rather than booting the full event loop per test function.
When it happens
Trigger: Running the binary where no display server is reachable: Docker containers, CI runners, systemd services, cron — DISPLAY and WAYLAND_DISPLAY unset; SSH sessions without X11 forwarding; a Wayland session whose compositor socket under $XDG_RUNTIME_DIR is gone; calling iced::run (or otherwise constructing an EventLoop) a second time in one process, e.g. two invocations in the same integration-test binary; on macOS, initializing the event loop off the main thread.
Common situations: GUI apps that also run in CI or containers without Xvfb; test harnesses that boot the full iced runtime per #[test] function; running the GUI binary over plain ssh; distro/minimal images where libxcb/libxkbcommon-x11 are missing so the X11 connect fails; switching sessions between X11 and Wayland with stale environment variables.
Related errors
AI-assisted analysis of iced-rs/iced@2cffa99b39 (2026-08-16).
Data as JSON: /api/errors/2d2f68efa10e297b.
Report an issue: GitHub.