elkowar/eww · critical
could not get default display
Error message
could not get default display
What it means
This panic comes from gtk::gdk::Display::default() returning None, meaning GDK could not open a connection to the display server. eww's daemon expects a valid default display (X11 or Wayland) so it can watch for monitor connect/disconnect events and reload the configuration when one appears.
Solutions
- Ensure a display server is reachable: verify `echo $DISPLAY` (X11) or `XDG_SESSION_TYPE=wayland` and that the daemon inherits the session environment (`dbus-launch`, `systemctl --user import-environment DISPLAY WAYLAND_DISPLAY`).
- Start eww from inside the graphical session (autostart/desktop entry) rather than from a login shell or system service.
- Under Wayland, use `eww daemon` with GDK Wayland support, or force the X11 backend via `GDK_BACKEND=x11` with XWayland running.
- If running headless intentionally, guard the call: fall back to skipping monitor watching when Display::default() is None instead of panicking.
- Check that GDK was compiled with a usable backend (`GDK_BACKEND=x11,wayland` debug) and that libgtk is the expected version.
Example fix
// before
let display = gtk::gdk::Display::default().expect("could not get default display");
display.connect_monitor_added(...);
// after
let display = match gtk::gdk::Display::default() {
Some(d) => d,
None => {
log::warn!("No default display available; monitor watching disabled");
return;
}
};
display.connect_monitor_added(...); Defensive patterns
Strategy: fallback
Validate before calling
// before starting the daemon
if std::env::var("DISPLAY").is_err() && std::env::var("WAYLAND_DISPLAY").is_err() {
eprintln!("No DISPLAY/WAYLAND_DISPLAY set; eww needs a graphical session");
std::process::exit(1);
} Type guard
fn has_default_display() -> bool { gtk::gdk::Display::default().is_some() } Try / catch
// Rust panics cannot be caught idiomatically here; use catch_unwind only as a last resort let result = std::panic::catch_unwind(connect_monitor_added_safe);
Prevention
- Start eww from within the graphical session, not cron/SSH/boot scripts
- Import session env into systemd user units: systemctl --user import-environment DISPLAY WAYLAND_DISPLAY
- Check `echo $DISPLAY` / `XDG_SESSION_TYPE` before launching
- Under Wayland ensure XWayland or a GDK wayland backend is available
When it happens
Trigger: Calling connect_monitor_added (during initialize_server) in an environment where no display is available: DISPLAY unset or pointing at a dead X server, Wayland without XWayland/GDK backend, running under systemd before graphical.target, SSH session without X forwarding, or DISPLAY set to a display that has shut down.
Common situations: Users starting `eww daemon` from cron/systemd user units before login, SSH shells, CI containers, or after logging out of the graphical session while the daemon keeps running. Wayland-only setups where GDK was built without the wayland backend.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- Error opening log file
- Error, trying to add multiple children to a…
- Failed to obtain toplevel window
- Could not get default gtk theme
- no pixbuf from theme.load_icon despite no error
AI-assisted analysis of elkowar/eww@48f5aa8b37 (2026-09-08).
Data as JSON: /api/errors/24805d36cce36374.
Report an issue: GitHub.
Appendix: source
Thrown at crates/eww/src/server.rs:142
Some(ui_event) = ui_recv.recv() => {
app.handle_command(ui_event).await;
}
else => break,
}
}
});
// allow the GTK main thread to do tokio things
let _g = tokio_handle.enter();
gtk::main();
log::info!("main application thread finished");
Ok(ForkResult::Child)
}
fn connect_monitor_added(ui_send: UnboundedSender<DaemonCommand>) {
let display = gtk::gdk::Display::default().expect("could not get default display");
display.connect_monitor_added({
move |_display: >k::gdk::Display, _monitor: >k::gdk::Monitor| {
log::info!("New monitor connected, reloading configuration");
let _ = reload_config_and_css(&ui_send);
}
});
}
fn reload_config_and_css(ui_send: &UnboundedSender<DaemonCommand>) -> Result<()> {
let (daemon_resp_sender, mut daemon_resp_response) = daemon_response::create_pair();
ui_send.send(DaemonCommand::ReloadConfigAndCss(daemon_resp_sender))?;
tokio::spawn(async move {
match daemon_resp_response.recv().await {
Some(daemon_response::DaemonResponse::Success(_)) => log::info!("Reloaded config successfully"),
Some(daemon_response::DaemonResponse::Failure(e)) => eprintln!("{}", e),
None => log::error!("No response to reload configuration-reload request"),
}
});View on GitHub (pinned to 48f5aa8b37)