{"record":{"id":"ad420a2767055329","repo":"nautechsystems/nautilus_trader","slug":"instrument-update-lock-poisoned","errorCode":null,"errorMessage":"instrument update lock poisoned","messagePattern":"instrument update lock poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/okx/src/data.rs","lineNumber":693,"sourceCode":"                    ts_init,\n                    funding_cache,\n                    &instruments_guard,\n                ) {\n                    Ok(Some(ws_msg)) => {\n                        dispatch_parsed_data(ws_msg, data_sender, instruments_by_symbol);\n                    }\n                    Ok(None) => {}\n                    Err(e) => log::error!(\"Failed to parse {channel:?} data: {e}\"),\n                }\n            }\n            OKXWsMessage::Instruments(okx_instruments) => {\n                let ts_init = clock.get_time_ns();\n                // Hold the instrument lock for the batch so a concurrent\n                // reconciliation cannot interleave diff, cache update, and publish\n                let _update_guard = instrument_update_lock\n                    .mutex\n                    .lock()\n                    .expect(\"instrument update lock poisoned\");\n\n                for okx_inst in okx_instruments {\n                    let inst_key = okx_inst.inst_id;\n                    let cached = instruments_by_symbol.get_cloned(&inst_key);\n                    let (margin_init, margin_maint, maker_fee, taker_fee) = cached\n                        .as_ref()\n                        .map_or((None, None, None, None), |instrument| {\n                            extract_fees_from_cached_instrument(instrument)\n                        });\n                    let status_action = okx_status_to_market_action(okx_inst.state);\n                    let is_live = matches!(okx_inst.state, OKXInstrumentStatus::Live);\n                    match parse_instrument_any(\n                        &okx_inst,\n                        margin_init,\n                        margin_maint,\n                        maker_fee,\n                        taker_fee,\n                        ts_init,","sourceCodeStart":675,"sourceCodeEnd":711,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/d1527c24afdf475115785557f89a55c3e336c51f/crates/adapters/okx/src/data.rs#L675-L711","documentation":"The OKX data engine holds a dedicated instrument-update mutex across each WebSocket instruments batch (diff, cache update, publish). This expect panics when that mutex is poisoned — some other thread panicked while holding it (e.g. inside the reconcile pass at line 1595 or request_instruments).","triggerScenarios":"handle_ws_message processing an OKX instruments channel batch after a panic occurred anywhere the same instrument_update_lock is held: reconcile_instruments, request_instruments, or request_instrument.","commonSituations":"A panic in instrument parsing/definition conversion under the lock (bad OKX payload edge case) poisons the lock; every subsequent WS instruments message then panics the data engine loop.","solutions":["Locate the original panic under instrument_update_lock (earlier in logs) and fix that payload/conversion bug","Keep only non-panicking operations under the guard; propagate Result through the batch handler","Consider `unwrap_or_else(|e| e.into_inner())` — the lock guards ordering, and data invariants may remain usable after a panic"],"exampleFix":"// before\nlet _update_guard = instrument_update_lock.mutex.lock().expect(\"instrument update lock poisoned\");\n// after\nlet _update_guard = instrument_update_lock.mutex.lock().unwrap_or_else(|e| e.into_inner());","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never panic inside the instruments batch handler; return Result and skip malformed instruments","Keep publish/cache work under the lock minimal and infallible","Recover via into_inner() plus write_seq staleness checks"],"tags":["rust","mutex-poisoned","okx","instruments","panic"],"backgroundTag":"mutex-poisoned","analyzedSha":"d1527c24afdf475115785557f89a55c3e336c51f","analyzedAt":"2026-08-27T04:01:12.327Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}