{"record":{"id":"1bfda39d8d7c2368","repo":"nautechsystems/nautilus_trader","slug":"ocm-state-lock-poisoned","errorCode":null,"errorMessage":"OCM state lock poisoned","messagePattern":"OCM state lock poisoned","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/adapters/betfair/src/execution.rs","lineNumber":3038,"sourceCode":"        };\n\n        let response =\n            list_current_orders_with_retry(http_client, &params, stream_session, session_refresh)\n                .await?;\n        let page_size = response.current_orders.len() as u32;\n\n        orders.extend(response.current_orders);\n\n        if !response.more_available {\n            break;\n        }\n\n        from_record += page_size;\n    }\n\n    let mut state = ocm_state\n        .lock()\n        .map_err(|_| anyhow::anyhow!(\"OCM state lock poisoned\"))?;\n    Ok(build_incremental_fill_reports(\n        &orders, &mut state, account_id, currency, ts_init,\n    ))\n}\n\nfn build_incremental_fill_reports(\n    orders: &[CurrentOrderSummary],\n    state: &mut OcmState,\n    account_id: AccountId,\n    currency: Currency,\n    ts_init: UnixNanos,\n) -> Vec<FillReport> {\n    let mut reports = Vec::new();\n\n    for order in orders {\n        let size_matched = order.size_matched.unwrap_or(Decimal::ZERO);\n        let size_voided = order.size_voided.unwrap_or(Decimal::ZERO);\n        let gross_matched = size_matched + size_voided;","sourceCodeStart":3020,"sourceCodeEnd":3056,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/betfair/src/execution.rs#L3020-L3056","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match reports_result {\n    Ok(reports) => { /* process */ }\n    Err(e) if e.to_string().contains(\"lock poisoned\") => {\n        log::error!(\"OCM state poisoned by earlier panic; restart required\");\n        // halt trading safely: cancel open orders if possible, then exit\n        std::process::exit(1);\n    }\n    Err(e) => log::warn!(\"OCM poll failed: {e}\"),\n}","preventionTips":["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."],"tags":["betfair","mutex","poisoned-lock","panic","ocm"],"backgroundTag":"mutex-poisoned","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}