zellij-org/zellij · critical · anyhow::Error
cannot acquire poisoned lock for {e:#?}
Error message
cannot acquire poisoned lock for {e:#?} What it means
Debug-mode variant of the ToAnyhow implementation for std::sync::PoisonError in zellij-utils/errors.rs. A Mutex/RwLock became poisoned because some thread panicked while holding it, and now every subsequent .lock() on that lock fails and is converted to this error (including the poison payload in DEBUG_MODE). The lock itself is fine; the poison flag records that a holder bailed out mid-critical-section, so guarded state may be inconsistent.
Source
Thrown at zellij-utils/src/errors.rs:976
msg
))
.with_context(|| context.to_string())
} else {
Err(anyhow::anyhow!("failed to send message to channel"))
.with_context(|| context.to_string())
}
},
}
}
}
impl<U> ToAnyhow<U> for Result<U, std::sync::PoisonError<U>> {
fn to_anyhow(self) -> anyhow::Result<U> {
match self {
Ok(val) => anyhow::Ok(val),
Err(e) => {
if *crate::consts::DEBUG_MODE.get().unwrap_or(&true) {
Err(anyhow::anyhow!("cannot acquire poisoned lock for {e:#?}"))
} else {
Err(anyhow::anyhow!("cannot acquire poisoned lock"))
}
},
}
}
}
}
View on GitHub (pinned to 98a0837077)
Solutions
- Find the original panic in the log (the first panic, not the poison errors) — fix or report that; the poison cascade is downstream
- Restart the zellij session; poison state cannot be cleared in-process
- Upgrade zellij; several panics-under-lock bugs were fixed over releases
- As a developer: keep critical sections panic-free (no unwrap/expect under locks) and shrink lock scope so a panic is unlikely inside it
Example fix
// before
let state = shared.lock().to_anyhow()?; // any later thread fails once poisoned
// after: one designated recovery point, deliberate policy
let state = match shared.lock() {
Ok(g) => g,
Err(poisoned) => {
log::error!("lock poisoned, recovering guard; state may be inconsistent");
poisoned.into_inner()
},
}; Defensive patterns
Strategy: fallback
Try / catch
use std::sync::{Mutex, PoisonError};
fn lock_or_recover<T>(m: &Mutex<T>) -> Result<std::sync::MutexGuard<'_, T>, PoisonError<std::sync::MutexGuard<'_, T>>> {
// deliberate policy at one designated recovery point: state guarded by this
// lock is tolerant to partial writes, so recover the guard instead of failing
m.lock().map_err(|p| { log::error!("lock poisoned, recovering: {p}"); p })
}
// usage: lock_or_recover(&shared).unwrap_or_else(|p| p.into_inner()).update(); Prevention
- Never unwrap/expect/index inside a critical section; compute risky values before locking
- Keep lock scopes minimal so panics cannot strike while holding the lock
- Centralize lock acquisition behind helpers with a single documented poison policy
- When you see this error, always fix the FIRST panic in the log — poison is only its shadow
When it happens
Trigger: Any panic (unwrap, index out of bounds, assertion) inside a critical section guarded by the affected Mutex/RwLock, followed by any other thread attempting to lock it and piping the result through .to_anyhow()?.
Common situations: A rendering/pane-handling bug panics once while holding a shared lock; every later operation touching that lock then fails with poisoned-lock errors, cascading across threads. Almost always a zellij-internal bug, not user misconfiguration.
Related errors
AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16).
Data as JSON: /api/errors/56ffbb37ae973fc3.
Report an issue: GitHub.