tauri-apps/tauri · error
poisoned window
Error message
poisoned window
What it means
Window::menu_lock() returns the per-window Mutex<Option<WindowMenu>> used by all menu operations — set_menu, remove_menu, hide_menu, show_menu, is_menu_visible and menu-event dispatch (crates/tauri/src/window/mod.rs:1320-1439). If any thread panics while that mutex is held, Rust poisons it, and the .expect("poisoned window") panics on the next menu operation for that Window. As with all poisoning, this message is the second failure; the root cause is an earlier panic inside a menu code path.
Source
Thrown at crates/tauri/src/window/mod.rs:1256
});
```
"####
)]
pub fn on_menu_event<F: Fn(&Window<R>, crate::menu::MenuEvent) + Send + Sync + 'static>(
&self,
f: F,
) {
self
.manager
.menu
.event_listeners
.lock()
.unwrap()
.insert(self.label().to_string(), Box::new(f));
}
pub(crate) fn menu_lock(&self) -> std::sync::MutexGuard<'_, Option<WindowMenu<R>>> {
self.menu.lock().expect("poisoned window")
}
#[cfg_attr(target_os = "macos", allow(dead_code))]
pub(crate) fn has_app_wide_menu(&self) -> bool {
self
.menu_lock()
.as_ref()
.map(|m| m.is_app_wide)
.unwrap_or(false)
}
#[cfg_attr(target_os = "macos", allow(dead_code))]
pub(crate) fn is_menu_in_use<I: PartialEq<MenuId>>(&self, id: &I) -> bool {
self
.menu_lock()
.as_ref()
.map(|m| id.eq(m.menu.id()))
.unwrap_or(false)View on GitHub (pinned to 52e4b6e71d)
Solutions
- Run with RUST_BACKTRACE=1 and identify the FIRST panic — it happened while the menu lock was held and is the real bug.
- Remove unwrap/expect from all menu-related code: menu building, find_by_id/get lookups, and on_menu_event handlers.
- Avoid re-entrant menu mutation: don't call set_menu from inside on_menu_event; defer via app.handle() + run_on_main_thread or spawn.
- Re-create the Window after fixing — the poisoned menu mutex is unrecoverable for the process.
Example fix
// before: unwrap in a menu handler can panic while menu machinery runs;
// the panic poisons the window menu mutex, later menu ops fail
window.on_menu_event(|w, e| {
let item = w.menu().unwrap().get(e.id()).unwrap().as_menuitem().unwrap();
item.set_enabled(false).unwrap();
});
// after: every lookup is fallible, nothing panics
window.on_menu_event(|w, e| {
let Some(menu) = w.menu() else { return };
if let Some(item) = menu.get(e.id()).and_then(|i| i.as_menuitem()) {
if let Err(err) = item.set_enabled(false) {
log::error!("failed to disable menu item: {err}");
}
}
}); Defensive patterns
Strategy: try-catch
Try / catch
use std::panic::{catch_unwind, AssertUnwindSafe};
// menu_lock() panics once the per-window menu mutex is poisoned;
// wrap menu operations so the UI can degrade instead of crashing
let outcome = catch_unwind(AssertUnwindSafe(|| {
window.set_menu(Some(menu))
}));
if outcome.is_err() {
log::error!("window menu mutex poisoned — menu disabled for this window");
} Prevention
- Treat all menu lookups (menu(), get(id), as_menuitem()) as fallible — no unwrap in on_menu_event handlers.
- Do not mutate the window menu re-entrantly from inside menu event handlers; defer with run_on_main_thread or spawn.
- Install a panic hook to capture the first panic — it identifies the code that actually poisoned the menu mutex.
- Validate menu item ids and native menu state before set_menu to avoid panics inside the locked section.
When it happens
Trigger: A panic while the per-window menu mutex is held: e.g. a panic during window.set_menu(...) (the WindowMenu is constructed inside the lock guard), a re-entrant set_menu/popup from inside on_menu_event that panics, native muda menu code panicking on the main thread, or panicking code run from hide_menu/show_menu dispatch closures. The 'poisoned window' expect then fires on any later menu() / set_menu(None) / hide_menu() / popup call on that window.
Common situations: Apps that rebuild menus dynamically (enable/disable items looked up by id with unwrap) and panic in a handler; swapping menus from inside menu event handlers; panics during context-menu popup on Linux/Windows; after the first panic, every menu interaction on that window crashes with 'poisoned window'.
Related errors
- poisoned window resources table
- poisoned window resources table
- poisoned window resources table
- Failed to render template
- view to be installed in window
AI-assisted analysis of tauri-apps/tauri@52e4b6e71d (2026-08-20).
Data as JSON: /api/errors/604ddc37a315b9e0.
Report an issue: GitHub.