linebender/druid · critical
failed to initialize the displays event loop!
Error message
failed to initialize the displays event loop!
What it means
The Wayland outputs/displays module builds a calloop event loop to track monitor (output) metadata changes, initializing a per-display BTreeMap cache. If calloop::EventLoop::try_new() fails while setting up this loop in current(), the code panics with expect and this message, aborting display-information initialization.
Solutions
- Raise the file-descriptor limit (`ulimit -n`) and audit for fd leaks in the process.
- Adjust sandbox/seccomp profiles to allow epoll syscalls; test by running unsandboxed.
- Free memory / check for OOM conditions at startup.
- Patch or upgrade so failure falls back to a cached/empty display list instead of panicking.
- Update druid-shell; newer revisions reworked wayland output handling.
Example fix
// before
let mut eventloop = calloop::EventLoop::try_new()
.expect("failed to initialize the displays event loop!");
// after
let mut eventloop = calloop::EventLoop::try_new()
.map_err(|e| tracing::error!("displays event loop init failed: {e}"))
.unwrap_or_default_fallback(); // degrade to polling display list once at startup Defensive patterns
Strategy: try-catch
Validate before calling
// preflight epoll availability before display enumeration
let epoll_ok = unsafe { libc::epoll_create1(0) } >= 0;
if !epoll_ok { /* fall back to one-shot display query */ } Try / catch
match calloop::EventLoop::try_new() {
Ok(el) => run_display_loop(el),
Err(e) => {
log::error!("displays loop init failed: {e}");
// fall back to a static snapshot of outputs
}
} Prevention
- Check fd limits and sandbox syscall filters before launching on Wayland.
- Verify display enumeration works when running outside Flatpak/Snap first.
- Upgrade druid-shell for revised wayland output handling.
- Watch for fd leaks via /proc/<pid>/fd growth in long sessions.
When it happens
Trigger: calloop EventLoop::try_new() returning Err — epoll_create1 failure from exhausted file descriptors, sandboxed syscall filters (seccomp), or memory pressure — when current() is called to enumerate displays on Wayland.
Common situations: Apps launched under Flatpak/Snap with restrictive sandboxing; systems at the fd limit (many open windows/sockets or leaking fds); embedded Wayland environments; multi-monitor setups querying display metadata at startup.
Related errors
- failed to initialize the keyboard event loop!
- keyboard event processing failed
- unexpected wayland event
- unrecognised key event
- attaching an already in-use surface
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/1d6b97d200990c92.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/wayland/outputs/mod.rs:38
) -> Result<calloop::channel::Channel<Event>, error::Error> {
tracing::debug!("detecting xdg outputs");
match output::detect(env) {
Ok(rx) => return Ok(rx),
Err(cause) => tracing::info!("unable to detect xdg outputs {:?}", cause),
}
Err(error::Error::string("unable to detect display outputs"))
}
pub(super) fn current() -> Result<Vec<Meta>, error::Error> {
let dispatcher = display::Dispatcher::default();
let rx = auto(&dispatcher)?;
let env = display::new(dispatcher)?;
let mut cache = std::collections::BTreeMap::new();
let mut eventloop: calloop::EventLoop<(
calloop::LoopSignal,
&mut std::collections::BTreeMap<String, Meta>,
)> = calloop::EventLoop::try_new().expect("failed to initialize the displays event loop!");
let signal = eventloop.get_signal();
let handle = eventloop.handle();
handle
.insert_source(rx, {
move |event, _ignored, (signal, cache)| {
let event = match event {
calloop::channel::Event::Msg(event) => event,
calloop::channel::Event::Closed => return signal.stop(),
};
match event {
Event::Located(meta) => {
cache.insert(meta.name.clone(), meta);
}
Event::Removed(meta) => {
cache.remove(&meta.name);
}
}View on GitHub (pinned to 0f8b1195e4)