{"record":{"id":"aaffcf2ed1af4655","repo":"wezterm/wezterm","slug":"raw-window-handle-only-callable-on-main-thread","errorCode":null,"errorMessage":"raw_window_handle only callable on main thread","messagePattern":"raw_window_handle only callable on main thread","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"window/src/os/wayland/window.rs","lineNumber":1655,"sourceCode":"        let handle = WaylandWindowHandle::new(\n            NonNull::new(self.surface().id().as_ptr() as _).expect(\"non-null\"),\n        );\n        unsafe { Ok(WindowHandle::borrow_raw(RawWindowHandle::Wayland(handle))) }\n    }\n}\n\nimpl HasDisplayHandle for WaylandWindow {\n    fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError> {\n        let conn = WaylandConnection::get().unwrap().wayland();\n        let backend = conn.connection.backend();\n        let handle = backend.display_handle()?;\n        Ok(unsafe { DisplayHandle::borrow_raw(handle.as_raw()) })\n    }\n}\n\nimpl HasWindowHandle for WaylandWindow {\n    fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError> {\n        let conn = Connection::get().expect(\"raw_window_handle only callable on main thread\");\n        let handle = conn\n            .wayland()\n            .window_by_id(self.0)\n            .expect(\"window handle invalid!?\");\n\n        let inner = handle.borrow();\n        let handle = inner.window_handle()?;\n        unsafe { Ok(WindowHandle::borrow_raw(handle.as_raw())) }\n    }\n}\n","sourceCodeStart":1637,"sourceCodeEnd":1666,"githubUrl":"https://github.com/wezterm/wezterm/blob/08e5e0afc6007567d3d131bc2ec1382423c0d2da/window/src/os/wayland/window.rs#L1637-L1666","documentation":"HasWindowHandle::window_handle on WaylandWindow starts with Connection::get(), a thread-local accessor that only holds a value on the GUI main thread where Connection::init ran. Calling any raw-window-handle API from another thread makes get() return None and this expect panics.","triggerScenarios":"Triggered by window_handle()/display_handle() invoked off the main thread — classic sources: wgpu surface creation from a worker, EGL/GL initialization on a spawned thread, or libraries that query raw handles lazily on arbitrary threads.","commonSituations":"Integrating wezterm's window crate into another framework; vendored copies used by GUI/graphics stacks that create resources on render threads; calling handle APIs after moving a window id into async tasks.","solutions":["Call window_handle()/display_handle() only on the thread that ran Connection::init (the main/GUI thread)","Capture the raw handle on the main thread and pass RawWindowHandle/RawDisplayHandle values (they are plain data) to worker threads instead of the object","If you control the embedder, dispatch handle queries through the main thread (e.g. promise::spawn::spawn_into_main_thread) and await the result"],"exampleFix":"// before: panics when run on a worker thread\nlet raw = window.window_handle()?.as_raw();\n\n// after: capture on the main thread, then send the raw value\nlet raw = window.window_handle()?.as_raw(); // main thread\nlet (tx, rx) = std::sync::mpsc::channel();\nstd::thread::spawn(move || { tx.send(render_with(raw)).unwrap(); });","handlingStrategy":"validation","validationCode":"// guard before querying, if you embed this crate\nfn on_main() -> bool { /* your main-thread token check */ true }\nassert!(on_main(), \"window_handle must be called on the main thread\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat raw-handle getters as main-thread-only API by convention","Capture RawWindowHandle/RawDisplayHandle once on the main thread and pass the values around","Dispatch any framework's handle queries through the main loop"],"tags":["wayland","raw-window-handle","threading","main-thread","panic"],"backgroundTag":"api-called-off-main-thread","analyzedSha":"08e5e0afc6007567d3d131bc2ec1382423c0d2da","analyzedAt":"2026-08-20T04:47:31.434Z","contentChangedAt":"2026-08-20T04:47:31.434Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}