nautechsystems/nautilus_trader · error
subscription state lock poisoned
Error message
subscription state lock poisoned
What it means
Subscription state (pending/subscribed topics) is guarded by an RwLock; read-side helpers (confirmed, pending_subscribe, len, is_empty, is_subscribed) take a read guard via lock_state_read and panic if the RwLock is poisoned by a panic in a write-side critical section.
Source
Thrown at crates/network/src/websocket/subscription.rs:475
// Add symbol-level subscriptions (skip marker)
for symbol in symbols {
if *symbol != marker {
topics.push(format!("{channel}{}{symbol}", self.delimiter));
}
}
}
// Sort so resubscription after a reconnect replays topics in the same sequence
// across runs; both the outer DashMap and the inner symbol sets are unordered.
topics.sort();
topics
}
fn lock_state_read(&self) -> RwLockReadGuard<'_, ()> {
self.state_lock
.read()
.expect("subscription state lock poisoned")
}
fn lock_state_write(&self) -> RwLockWriteGuard<'_, ()> {
self.state_lock
.write()
.expect("subscription state lock poisoned")
}
}
/// Splits a topic into channel and optional symbol using the specified delimiter.
#[must_use]
pub fn split_topic(topic: &str, delimiter: char) -> (&str, Option<&str>) {
topic
.split_once(delimiter)
.map_or((topic, None), |(channel, symbol)| (channel, Some(symbol)))
}
fn snapshot(map: &DashMap<Ustr, AHashSet<Ustr>>) -> SubscriptionSnapshot {View on GitHub (pinned to d1527c24af)
Solutions
- Fix the panic inside the write-side critical sections (keep callbacks/user code outside the guard)
- Read guards on a poisoned RwLock can safely recover with into_inner() since reads don't mutate — switch lock_state_read to unwrap_or_else(|e| e.into_inner())
- Add tests that panic in a write path and assert reads still work
Example fix
// before
self.state_lock.read().expect("subscription state lock poisoned")
// after
self.state_lock.read().unwrap_or_else(|e| e.into_inner()) Defensive patterns
Strategy: fallback
Prevention
- Move user callbacks and logging outside the write guard
- Recover reads from poison with into_inner(); reads cannot worsen torn state
- Assert in tests that a write-path panic leaves queries functional
When it happens
Trigger: Any read of subscription state after a thread panicked while holding the write lock — e.g. mark_subscribe/confirm_subscribe mutating topic maps hit an inconsistent state or a downstream callback panicked under the guard.
Common situations: A WS user handler or metrics callback panicking during confirm_subscribe poisons the lock; afterwards every status query (is_subscribed etc.) panics, crashing the actor's message loop.
Related errors
- instrument update lock poisoned
- option_summary_family_subs mutex poisoned
- rate limiter decision lock poisoned
- InstrumentState channel requires kind and currency parameter
- std::mem::take(&mut self.shutdown_errors).join("; ")
AI-assisted analysis of nautechsystems/nautilus_trader@d1527c24af (2026-08-27).
Data as JSON: /api/errors/c36fa2f0b8eac43d.
Report an issue: GitHub.