{"record":{"id":"69ea007a96d6c0b4","repo":"tauri-apps/tauri","slug":"poisoned-window-resources-table-69ea00","errorCode":null,"errorMessage":"poisoned window resources table","messagePattern":"poisoned window resources table","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tauri/src/webview/webview_window.rs","lineNumber":2795,"sourceCode":"  ///     webview_window.unlisten(handler);\n  ///\n  ///     Ok(())\n  /// });\n  /// ```\n  fn unlisten(&self, id: EventId) {\n    self.manager().unlisten(id)\n  }\n}\n\nimpl<R: Runtime> Emitter<R> for WebviewWindow<R> {}\n\nimpl<R: Runtime> Manager<R> for WebviewWindow<R> {\n  fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {\n    self\n      .webview\n      .resources_table\n      .lock()\n      .expect(\"poisoned window resources table\")\n  }\n}\n\nimpl<R: Runtime> ManagerBase<R> for WebviewWindow<R> {\n  fn manager(&self) -> &AppManager<R> {\n    self.webview.manager()\n  }\n\n  fn manager_owned(&self) -> Arc<AppManager<R>> {\n    self.webview.manager_owned()\n  }\n\n  fn runtime(&self) -> RuntimeOrDispatch<'_, R> {\n    self.window.runtime()\n  }\n\n  fn managed_app_handle(&self) -> &AppHandle<R> {\n    self.webview.managed_app_handle()","sourceCodeStart":2777,"sourceCodeEnd":2813,"githubUrl":"https://github.com/tauri-apps/tauri/blob/52e4b6e71d8632a7e648f866c442e287ecddee34/crates/tauri/src/webview/webview_window.rs#L2777-L2813","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Run with RUST_BACKTRACE=1 and locate the FIRST panic — that is what poisoned the lock; this message is only the second failure.","Make every Resource::drop and every block holding a resources_table() guard panic-free: replace unwrap/expect with logged error handling.","Narrow lock scopes: clone Arcs out of the table and drop the guard before doing anything fallible.","Restart the app after the fix — poisoning is permanent for the process; there is no un-poison API on these locks."],"exampleFix":"// before: command panics while a resources_table() guard is alive,\n// poisoning the table shared by Webview and WebviewWindow\n#[tauri::command]\nfn read_stream(state: tauri::State<AppState>, rid: u32) -> Vec<u8> {\n  let table = state.app.webview_window(\"main\").unwrap().resources_table();\n  let res = table.get::<MyStream>(rid).unwrap(); // panics -> poisons\n  res.read_all()\n}\n\n// after: no guard held across fallible lookups, no unwrap\n#[tauri::command]\nfn read_stream(app: tauri::AppHandle, rid: u32) -> Result<Vec<u8>, String> {\n  let win = app.webview_window(\"main\").ok_or(\"window missing\")?;\n  let stream = {\n    let table = win.resources_table();\n    table.get::<MyStream>(rid).map_err(|e| e.to_string())?\n  }; // guard dropped here\n  stream.read_all().map_err(|e| e.to_string())\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"use std::panic::{catch_unwind, AssertUnwindSafe};\n\n// WebviewWindow shares the inner webview's resource table; a poisoned\n// table panics here — contain and recover by rebuilding the window\nmatch catch_unwind(AssertUnwindSafe(|| webview_window.resources_table())) {\n  Ok(table) => { /* safe to use */ }\n  Err(_) => {\n    log::error!(\"WebviewWindow resource table poisoned — re-create the window/webview\");\n  }\n}","preventionTips":["Make all custom Resource::drop impls panic-free (log instead of unwrap/expect).","Add a startup panic hook that logs the first panic — that is the real poisoner; 'poisoned window resources table' is the follow-on symptom.","Do not hold resources_table() guards across ?-propagating or unwrapping code; extract Arcs and drop guards early.","When upgrading tauri or fs/http plugins, re-test long-running resource flows (streams, downloads) for panics that poison the shared table."],"tags":["rust","mutex","poisoning","panic","resources","webview-window","manager"],"backgroundTag":"mutex-poisoning","analyzedSha":"52e4b6e71d8632a7e648f866c442e287ecddee34","analyzedAt":"2026-08-20T13:59:20.734Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}