tauri-apps/tauri · error

poisoned window resources table

Error message

poisoned window resources table

What it means

WebviewWindow implements Manager::resources_table() by locking the inner Webview's ResourceTable mutex and calling .expect("poisoned window resources table"). A std::sync::Mutex becomes poisoned when a thread panics while holding it; all later locks then fail. So this panic on a WebviewWindow means some earlier panic poisoned the shared webview's resource table — the expect is the follow-on crash, not the original fault. Because Webview and WebviewWindow share the same table, poisoning through either type breaks resource IPC for both.

Source

Thrown at crates/tauri/src/webview/webview_window.rs:2795

  ///     webview_window.unlisten(handler);
  ///
  ///     Ok(())
  /// });
  /// ```
  fn unlisten(&self, id: EventId) {
    self.manager().unlisten(id)
  }
}

impl<R: Runtime> Emitter<R> for WebviewWindow<R> {}

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

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

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

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

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

View on GitHub (pinned to 52e4b6e71d)

Solutions

  1. Run with RUST_BACKTRACE=1 and locate the FIRST panic — that is what poisoned the lock; this message is only the second failure.
  2. Make every Resource::drop and every block holding a resources_table() guard panic-free: replace unwrap/expect with logged error handling.
  3. Narrow lock scopes: clone Arcs out of the table and drop the guard before doing anything fallible.
  4. Restart the app after the fix — poisoning is permanent for the process; there is no un-poison API on these locks.

Example fix

// before: command panics while a resources_table() guard is alive,
// poisoning the table shared by Webview and WebviewWindow
#[tauri::command]
fn read_stream(state: tauri::State<AppState>, rid: u32) -> Vec<u8> {
  let table = state.app.webview_window("main").unwrap().resources_table();
  let res = table.get::<MyStream>(rid).unwrap(); // panics -> poisons
  res.read_all()
}

// after: no guard held across fallible lookups, no unwrap
#[tauri::command]
fn read_stream(app: tauri::AppHandle, rid: u32) -> Result<Vec<u8>, String> {
  let win = app.webview_window("main").ok_or("window missing")?;
  let stream = {
    let table = win.resources_table();
    table.get::<MyStream>(rid).map_err(|e| e.to_string())?
  }; // guard dropped here
  stream.read_all().map_err(|e| e.to_string())
}
Defensive patterns

Strategy: try-catch

Try / catch

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

// WebviewWindow shares the inner webview's resource table; a poisoned
// table panics here — contain and recover by rebuilding the window
match catch_unwind(AssertUnwindSafe(|| webview_window.resources_table())) {
  Ok(table) => { /* safe to use */ }
  Err(_) => {
    log::error!("WebviewWindow resource table poisoned — re-create the window/webview");
  }
}

Prevention

When it happens

Trigger: A panic anywhere while the inner webview's resource-table mutex is held — most commonly a panicking Resource::drop (resources are destroyed while the table lock is held), a panic in a plugin registering/removing resources (fs streams, http response bodies), or a panicking command that obtained a resources_table() guard — then any later call that touches resources through the WebviewWindow (invoke on commands using taururi-plugin-fs/http, resource resolution by rid).

Common situations: Custom Resource impls with unwrap/expect in Drop; file-streaming apps that panic mid-transfer and then crash on every subsequent resource IPC; version upgrades of tauri or resource-using plugins changing drop/registration order; panics inside event handlers or commands that walk the resource table.

Related errors


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