A poisoned mutex gives you a reason to inspect shared state. Rust marks a mutex as poisoned when it recognizes that a thread panicked while holding the lock. A later call to lock() still acquires the lock, but returns an error containing the guard. Rust treats the protected value as potentially inconsistent because the panic may have interrupted an update. Rust’s Mutex documentation describes poisoning as an advisory warning.
The useful question is what must be true about the value before another thread uses it. Those conditions are its invariants. A lock controls access while a guard is held. It cannot, by itself, tell you whether the last update finished.
A panic can interrupt an update
Imagine a shared queue with a vector of items and a separate count. Its invariant is that the count equals the vector’s length. An operation might add an item and then update the count. If it panics between those steps, the next thread can acquire the mutex and see a mismatched pair. This is an example of a possible interrupted update, not a claim about every panic.
A panic might also happen before either field changes, leaving the value consistent. The poison flag does not record which statements ran or which fields changed. It records that Rust recognized a panic while the lock was held. That is why the documentation says the data is likely tainted, rather than declaring it invalid in every case.
For the queue, recovery can start by checking the count against the vector’s length. A different type needs a different check. A balance and its transaction history might need to agree. A map and a secondary index might need to contain matching keys. The mutex cannot supply those rules. The code that defines and updates the shared value must supply them.
The error still contains the guard
The result from Mutex::lock() is easy to misread. On a poison error, the caller has acquired the lock. The error contains the guard that provides access to the protected value. Other threads do not gain access simply because lock() returned Err.
PoisonError::into_inner() extracts that guard. This makes inspection and repair possible. It also makes careless recovery possible. Calling into_inner() and carrying on with normal work skips the decision the poison flag was meant to prompt.
Calling unwrap() makes a different decision. It panics on a poison error, which can be appropriate when the program has no safe recovery path. The standard library documentation shows this pattern. Choose it deliberately for a value whose invariants you cannot verify or restore at that point. If the application needs to keep serving work, define a recovery policy for that particular value instead.
Recovery depends on the value
For the example queue, correcting the count from the vector’s length may restore the stated invariant, provided the items themselves are valid. If an interrupted operation could leave an incomplete item, that count check is insufficient. Recovery must cover every property later code relies on. This follows from the example’s data model; Rust cannot infer it from the mutex.
There are several reasonable outcomes after inspection. Keep the value when its invariants hold. Repair it when the code can establish a valid state. Replace it with a known good value when replacement is acceptable. Otherwise, return an error or stop the affected operation. Which outcome is safe depends on what the shared value represents and what work may have been interrupted.
After a successful repair, Mutex::clear_poison() clears the poisoned state. Its documentation gives both replacement with a known good value and inspection of a consistent value as examples. Clearing the flag is a declaration by your code that recovery is complete. The method does not inspect the data or repair it for you.
An unpoisoned lock is not a proof
Poisoning is advisory in the other direction too. The Mutex documentation describes cases where panic detection can miss an event, including unusual panic contexts and foreign exceptions. It explicitly warns that unsafe code cannot rely on poisoning for soundness. Design essential invariants so they remain safe even when the poison flag does not appear.
A separate call to is_poisoned() is also a weak basis for a decision while other threads are active. The method documentation says the status can change at any time. Acquire the guard and handle the result of that acquisition. Then assess the protected value under the lock, using checks suited to that value.
What to do
- Write down the invariants for the value inside the mutex. Include relationships between fields and any assumptions made by readers.
- When
lock()returns a poison error, decide whether the operation can safely inspect or repair the value. UsePoisonError::into_inner()to obtain the guard when it can. - Check all relevant invariants while holding the guard. Repair or replace the value only when that establishes a valid state. Otherwise, report failure through the application’s error path.
- Call
clear_poison()after recovery is complete. Keep the guard until the inspection and repair are finished; dropping it unlocks the mutex. - Review update paths that can panic while holding the lock. Where practical, prepare changes before modifying shared state, so each locked update has fewer intermediate states to recover from.

The Campfire
No commentsNobody has pulled up a log by this one yet. Be the first to say what you make of it.
Held for the desk. It appears after a look.