tauri-apps/tauri · error

poisoned window resources table

Error message

poisoned window resources table

What it means

Window implements Manager::resources_table() by locking its own std::sync::Mutex around the ResourceTable and calling .expect("poisoned window resources table"). Rust poisons a mutex when a thread panics while holding it, making every subsequent lock fail. This panic therefore reports that an earlier panic poisoned the Window's resource table; the message appears on the next resource access and repeats for every later one until the process ends.

Source

Thrown at crates/tauri/src/window/mod.rs:1072

  fn hash<H: Hasher>(&self, state: &mut H) {
    self.window.label.hash(state)
  }
}

impl<R: Runtime> Eq for Window<R> {}
impl<R: Runtime> PartialEq for Window<R> {
  /// Only use the [`Window`]'s label to compare equality.
  fn eq(&self, other: &Self) -> bool {
    self.window.label.eq(&other.window.label)
  }
}

impl<R: Runtime> Manager<R> for Window<R> {
  fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
    self
      .resources_table
      .lock()
      .expect("poisoned window resources table")
  }
}

impl<R: Runtime> ManagerBase<R> for Window<R> {
  fn manager(&self) -> &AppManager<R> {
    &self.manager
  }

  fn manager_owned(&self) -> Arc<AppManager<R>> {
    self.manager.clone()
  }

  fn runtime(&self) -> RuntimeOrDispatch<'_, R> {
    RuntimeOrDispatch::Dispatch(self.window.dispatcher.clone())
  }

  fn managed_app_handle(&self) -> &AppHandle<R> {
    &self.app_handle

View on GitHub (pinned to 52e4b6e71d)

Solutions

  1. Enable RUST_BACKTRACE=1 and find the FIRST panic in the log — it is the actual poisoner; this expect is downstream noise.
  2. Remove every potential panic from Resource::drop and from code paths holding the resources_table() guard (replace unwrap/expect with error returns or logging).
  3. Keep guards short-lived: fetch the Arc, drop the guard, then do fallible work.
  4. Restart the application once fixed — the poisoned mutex never recovers in-process.

Example fix

// before: unwrap inside Drop poisons the window resources table
impl Resource for TempBuffer {
  fn drop(&mut self) {
    std::fs::remove_file(&self.path).expect("temp file removed");
  }
}

// after: Drop must not panic
impl Resource for TempBuffer {
  fn drop(&mut self) {
    if let Err(e) = std::fs::remove_file(&self.path) {
      log::warn!("failed to remove temp file {}: {e}", self.path.display());
    }
  }
}
Defensive patterns

Strategy: try-catch

Try / catch

use std::panic::{catch_unwind, AssertUnwindSafe};

// menu-independent guard: wrap resource-table access on Window so a
// poisoned mutex degrades gracefully instead of crashing the thread
match catch_unwind(AssertUnwindSafe(|| window.resources_table())) {
  Ok(table) => { /* use the table */ }
  Err(_) => {
    log::error!("Window resource table poisoned — restart required");
  }
}

Prevention

When it happens

Trigger: Any panic occurring while a thread holds this Window's resources-table mutex: a panicking Resource::drop (drops run while the table lock is held), a panic in plugin resource registration/lookup (fs, http), or a command/event handler that panics while holding a resources_table() guard. The 'poisoned window resources table' panic then fires on the next resource-related IPC for this Window.

Common situations: Custom resource types with panicking Drop impls; apps that stream large files and hit a mid-stream panic, after which all resource IPC on that window crashes; tauri/plugin version bumps that altered drop order; unwrapping resource lookups by rid in commands.

Related errors


AI-assisted analysis of tauri-apps/tauri@52e4b6e71d (2026-08-20). Data as JSON: /api/errors/cc3fa7fad2a32e5e. Report an issue: GitHub.