zellij-org/zellij · error

cannot acquire poisoned lock for

Error message

cannot acquire poisoned lock for {e:#?}

What it means

Produced by the ToAnyhow impl for Result<_, std::sync::PoisonError<U>> when locking a std::sync::Mutex (or RwLock) whose poison flag is set: some thread panicked while holding the lock, so every subsequent .lock().to_anyhow() fails with this message. The DEBUG_MODE variant prints the PoisonError itself. It is always a secondary error — the original panic is the real fault.

Solutions

  1. Find the FIRST panic in the log/backtrace (RUST_BACKTRACE=1) — fix or report that; the poison message only tells you which lock was held.
  2. Kill and restart the wedged session (zellij kill-session <name>) — poisoned state does not recover in-process.
  3. If reproducible, reduce to the key/mouse sequence or layout that triggers the original panic and file an upstream issue with both backtraces.
  4. Update zellij — the panic that poisons the lock is usually already fixed.

Example fix

// before: panic while holding the lock poisons it for everyone
let state = shared.lock().unwrap();
do_work_that_may_panic(&state); // panic here poisons the mutex

// after: keep panics out of the critical section
let snapshot = shared.lock().unwrap().clone();
do_work_that_may_panic(&snapshot); // lock already released
Defensive patterns

Strategy: fallback

Try / catch

// recovering shared state after poisoning (data may be mid-update: validate!)
let guard = match mutex.lock() {
    Ok(g) => g,
    Err(poisoned) => {
        log::error!("mutex poisoned by earlier panic; recovering guard");
        poisoned.into_inner()
    },
};

Prevention

When it happens

Trigger: Any earlier panic inside a critical section (unwrapped Option/Result, index out of bounds, assertion) poisons the mutex; the next thread to acquire the lock gets Err(PoisonError) and converts it via to_anyhow(). In zellij this guards shared state like session/server bookkeeping.

Common situations: A rendering, input, or plugin thread panicked (often visible earlier in the log) and afterwards every user of that shared state reports 'cannot acquire poisoned lock'; typically the session becomes unresponsive and needs to be killed.

Related errors


AI-assisted analysis of zellij-org/zellij@bf8d23a4f7 (2026-08-19). Data as JSON: /api/errors/56ffbb37ae973fc3. Report an issue: GitHub.

Appendix: source

Thrown at zellij-utils/src/errors.rs:986

                            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 bf8d23a4f7)