linebender/druid · error
unexpected wayland event
Error message
unexpected wayland event: {evt:?} What it means
During `sync()`'s roundtrip, the Wayland connection expects only reply events, but the server sent an unexpected event before the roundtrip finished. Because pre-loop events cannot be routed to a window, the code panics rather than dropping them silently.
Solutions
- Only call `sync()` during setup, before the event loop starts
- Route events through the running loop's dispatcher after startup instead of syncing
- Update wayland-backend/compositor handling if a legit new event type arrives; patch the panic to log-and-ignore
Example fix
// before let app = Application::new()?; app.run(None); app.sync()?; // loop already running // after let app = Application::new()?; app.sync()?; // before run app.run(None);
Defensive patterns
Strategy: validation
Validate before calling
// Guard sync behind a lifecycle check in caller code:
if !event_loop_started { app.sync()?; } Type guard
fn sync_allowed(loop_started: bool) -> bool { !loop_started } Prevention
- Call `sync()` only during initialization, before `run()`
- Use the running loop's event dispatch instead of sync after startup
- Wrap sync in setup-only code paths guarded by lifecycle flags
When it happens
Trigger: Calling `sync()` after the event loop has started (its documented precondition), or the compositor dispatching an unhandled event type during a `sync_roundtrip` callback.
Common situations: Calling `Application::sync` at runtime after `run` started the loop; compositor quirks sending events during the initial roundtrip.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- keyboard event processing failed
- failed to initialize the keyboard event loop!
- failed to initialize the displays event loop!
- unrecognised key event
- attaching an already in-use surface
AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10).
Data as JSON: /api/errors/40649dd4d0f62651.
Report an issue: GitHub.
Appendix: source
Thrown at druid-shell/src/backend/wayland/application.rs:446
fn zwlr_layershell_v1(&self) -> Option<wl::Main<ZwlrLayerShellV1>> {
self.zwlr_layershell_v1.clone()
}
}
impl Data {
pub(crate) fn set_cursor(&self, cursor: &mouse::Cursor) {
self.pointer.replace(cursor);
}
/// Send all pending messages and process all received messages.
///
/// Don't use this once the event loop has started.
pub(crate) fn sync(&self) -> Result<(), Error> {
self.wayland
.queue
.borrow_mut()
.sync_roundtrip(&mut (), |evt, _, _| {
panic!("unexpected wayland event: {evt:?}")
})
.map_err(Error::fatal)?;
Ok(())
}
fn current_window_id(&self) -> u64 {
static DEFAULT: u64 = 0_u64;
*self.active_surface_id.borrow().front().unwrap_or(&DEFAULT)
}
pub(super) fn acquire_current_window(&self) -> Option<WindowHandle> {
self.handles
.borrow()
.get(&self.current_window_id())
.cloned()
}
fn handle_timer_event(&self, _token: TimerToken) {View on GitHub (pinned to 0f8b1195e4)