linebender/druid · critical

wayland should use unique object IDs

Error message

wayland should use unique object IDs

What it means

This panic fires in the wayland backend's surface handling when a WaylandProxySurface handle with an object ID that is already present in the handles map is inserted. The library treats wayland object IDs as globally unique, so a duplicate ID means an internal invariant violation in handle bookkeeping (handles.borrow_mut().insert(...) returned Some). It is an internal consistency check, not a user-facing validation error.

Solutions

  1. Update druid/druid-shell to the latest version; duplicate-ID bookkeeping bugs in the wayland backend have been fixed over time.
  2. Check that you are not cloning or re-registering the same WindowHandle/SurfaceHandle; ensure old handles are dropped before new ones are created for the same surface.
  3. Test under a mainstream compositor (sway, GNOME, KDE) and current wayland/wayland-client crate versions to rule out compositor-specific ID reuse.
  4. If reproducible on latest code, file a druid issue with a minimal repro, WAYLAND_DEBUG=1 log, and compositor/version details.

Example fix

// before: registering a second handle with the same id
handles.borrow_mut().insert(handle.id(), handle.clone());
// after: remove any stale entry (or assert) before inserting
let prev = handles.borrow_mut().remove(&handle.id());
debug_assert!(prev.is_none(), "surface id {} reused before cleanup", handle.id());
handles.borrow_mut().insert(handle.id(), handle.clone());
Defensive patterns

Strategy: type-guard

Validate before calling

// before registering a surface handle
let already = appdata.handles.borrow().contains_key(&handle.id());
if already { /* evict or skip instead of panicking */ }

Type guard

fn handle_is_fresh(handles: &RefCell<HashMap<u32, Handle>>, h: &Handle) -> bool {
    !handles.borrow().contains_key(&h.id())
}

Try / catch

// this is a panic, not a Result; recover only at a process/task boundary
let result = std::panic::catch_unwind(|| register_surface(handle.clone()));
if result.is_err() { log::error!("wayland surface registration panicked"); }

Prevention

When it happens

Trigger: Calling the wayland surface registration path (around window.rs:564) twice with the same wayland surface/proxy object ID, e.g. a surface handle being re-registered after being recreated or cloned without the old entry being removed, or a wayland compositor/registrar returning a recycled object ID that was not cleaned up.

Common situations: Running druid apps under wayland with unusual compositors or wayland protocol versions where surface IDs get reused; bugs in the backend's handle-drop/cleanup path leaving stale entries in appdata.handles; embedding or re-creating windows rapidly so a new surface reuses a live ID.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10). Data as JSON: /api/errors/368a724daf36956d. Report an issue: GitHub.

Appendix: source

Thrown at druid-shell/src/backend/wayland/window.rs:564

            let surface =
                surfaces::layershell::Surface::new(appdata.clone(), winhandle, self.config.clone());

            let handle = WindowHandle::new(
                surface.clone(),
                surfaces::surface::Dead,
                surface.clone(),
                surface.clone(),
                self.appdata.clone(),
            );

            if appdata
                .handles
                .borrow_mut()
                .insert(handle.id(), handle.clone())
                .is_some()
            {
                panic!("wayland should use unique object IDs");
            }
            appdata
                .active_surface_id
                .borrow_mut()
                .push_front(handle.id());

            surface.with_handler({
                let handle = handle.clone();
                move |winhandle| winhandle.connect(&handle.into())
            });

            Ok(handle)
        }
    }
}

#[allow(unused)]
pub mod popup {

View on GitHub (pinned to 0f8b1195e4)