nautechsystems/nautilus_trader · critical
OCM state lock poisoned
Error message
OCM state lock poisoned
What it means
ocm_state is a std::sync::Mutex<OcmState> shared by the OCM (order change message) reconciliation path; lock() returned a PoisonError, meaning some thread panicked while holding this mutex. From that point every OCM poll (fetch_fill_reports_via_http) fails with this message until the process restarts. The real defect is the earlier panic in the logs, not this error.
Source
Thrown at crates/adapters/betfair/src/execution.rs:3038
};
let response =
list_current_orders_with_retry(http_client, ¶ms, stream_session, session_refresh)
.await?;
let page_size = response.current_orders.len() as u32;
orders.extend(response.current_orders);
if !response.more_available {
break;
}
from_record += page_size;
}
let mut state = ocm_state
.lock()
.map_err(|_| anyhow::anyhow!("OCM state lock poisoned"))?;
Ok(build_incremental_fill_reports(
&orders, &mut state, account_id, currency, ts_init,
))
}
fn build_incremental_fill_reports(
orders: &[CurrentOrderSummary],
state: &mut OcmState,
account_id: AccountId,
currency: Currency,
ts_init: UnixNanos,
) -> Vec<FillReport> {
let mut reports = Vec::new();
for order in orders {
let size_matched = order.size_matched.unwrap_or(Decimal::ZERO);
let size_voided = order.size_voided.unwrap_or(Decimal::ZERO);
let gross_matched = size_matched + size_voided;View on GitHub (pinned to a4b06ed870)
Solutions
- Search logs backwards from this error for the originating panic and its backtrace; fix or report that panic.
- Restart the trading node — a poisoned std Mutex never recovers in-process.
- If reproducible, capture the CurrentOrderSummary/stream update that triggered the panic and file an issue with the adapter maintainers.
Defensive patterns
Strategy: try-catch
Try / catch
match reports_result {
Ok(reports) => { /* process */ }
Err(e) if e.to_string().contains("lock poisoned") => {
log::error!("OCM state poisoned by earlier panic; restart required");
// halt trading safely: cancel open orders if possible, then exit
std::process::exit(1);
}
Err(e) => log::warn!("OCM poll failed: {e}"),
} Prevention
- Treat any panic in the Betfair adapter as critical: run the node with panic backtraces and log capture enabled.
- Monitor for the original panic — 'lock poisoned' is always a symptom, not the cause.
- Supervise the node process (systemd/k8s) so a poisoned state triggers a clean restart.
- Report the triggering payload to maintainers so the panic is fixed.
When it happens
Trigger: Any panic inside a critical section holding ocm_state (fill-report building, customer_order_ref registration in the submit paths) poisons the lock; a subsequent generate_fill_reports call then hits the poisoned lock at execution.rs:3038.
Common situations: An upstream panic (decimal parse, unwrap, index) during order reconciliation masks itself as 'lock poisoned' on all later OCM polls; common after an unexpected venue payload triggers a panic in the parsing path.
Related errors
- failed to resolve positive quantity for current order {} (or
- height must be positive, was {self.height}
- pandas is required for report generation; install it with `p
- pandas is required for visualization; install it with `pip i
- {name} must not be None
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/1bfda39d8d7c2368.
Report an issue: GitHub.