{"record":{"id":"368a724daf36956d","repo":"linebender/druid","slug":"wayland-should-use-unique-object-ids","errorCode":null,"errorMessage":"wayland should use unique object IDs","messagePattern":"wayland should use unique object IDs","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"druid-shell/src/backend/wayland/window.rs","lineNumber":564,"sourceCode":"\n            let surface =\n                surfaces::layershell::Surface::new(appdata.clone(), winhandle, self.config.clone());\n\n            let handle = WindowHandle::new(\n                surface.clone(),\n                surfaces::surface::Dead,\n                surface.clone(),\n                surface.clone(),\n                self.appdata.clone(),\n            );\n\n            if appdata\n                .handles\n                .borrow_mut()\n                .insert(handle.id(), handle.clone())\n                .is_some()\n            {\n                panic!(\"wayland should use unique object IDs\");\n            }\n            appdata\n                .active_surface_id\n                .borrow_mut()\n                .push_front(handle.id());\n\n            surface.with_handler({\n                let handle = handle.clone();\n                move |winhandle| winhandle.connect(&handle.into())\n            });\n\n            Ok(handle)\n        }\n    }\n}\n\n#[allow(unused)]\npub mod popup {","sourceCodeStart":546,"sourceCodeEnd":582,"githubUrl":"https://github.com/linebender/druid/blob/0f8b1195e4e073f9597f2865299c3d18f8e4005f/druid-shell/src/backend/wayland/window.rs#L546-L582","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Update druid/druid-shell to the latest version; duplicate-ID bookkeeping bugs in the wayland backend have been fixed over time.","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.","Test under a mainstream compositor (sway, GNOME, KDE) and current wayland/wayland-client crate versions to rule out compositor-specific ID reuse.","If reproducible on latest code, file a druid issue with a minimal repro, WAYLAND_DEBUG=1 log, and compositor/version details."],"exampleFix":"// before: registering a second handle with the same id\nhandles.borrow_mut().insert(handle.id(), handle.clone());\n// after: remove any stale entry (or assert) before inserting\nlet prev = handles.borrow_mut().remove(&handle.id());\ndebug_assert!(prev.is_none(), \"surface id {} reused before cleanup\", handle.id());\nhandles.borrow_mut().insert(handle.id(), handle.clone());","handlingStrategy":"type-guard","validationCode":"// before registering a surface handle\nlet already = appdata.handles.borrow().contains_key(&handle.id());\nif already { /* evict or skip instead of panicking */ }","typeGuard":"fn handle_is_fresh(handles: &RefCell<HashMap<u32, Handle>>, h: &Handle) -> bool {\n    !handles.borrow().contains_key(&h.id())\n}","tryCatchPattern":"// this is a panic, not a Result; recover only at a process/task boundary\nlet result = std::panic::catch_unwind(|| register_surface(handle.clone()));\nif result.is_err() { log::error!(\"wayland surface registration panicked\"); }","preventionTips":["Always drop/destroy surface handles before their IDs can be reused.","Avoid keeping stale WindowHandle clones across window recreate cycles.","Pin druid-shell and wayland-client versions and test on mainstream compositors.","Run with WAYLAND_DEBUG=1 when debugging surface lifecycle issues."],"tags":["wayland","panic","internal-invariant","window-management"],"backgroundTag":"internal-invariant-violation","analyzedSha":"0f8b1195e4e073f9597f2865299c3d18f8e4005f","analyzedAt":"2026-09-10T14:27:34.582Z","contentChangedAt":"2026-09-10T14:27:34.582Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}