{"record":{"id":"4e65f86737145261","repo":"nautechsystems/nautilus_trader","slug":"timed-out-joining-websocket-handler-task-after-abo","errorCode":null,"errorMessage":"Timed out joining WebSocket handler task after abort: {error}","messagePattern":"Timed out joining WebSocket handler task after abort: (.+?)","errorType":"exception","errorClass":"Error::Io(std::io::Error)","httpStatus":null,"severity":"error","filePath":"crates/adapters/okx/src/websocket/client.rs","lineNumber":1060,"sourceCode":"\n        // Wipe per-base-pair refcounts so a subsequent reconnect can re-arm\n        // the index-tickers channel. Otherwise the stale count short-circuits\n        // every future `subscribe_index_prices` call and the feed stays dark.\n        self.index_pair_subscribers.clear();\n\n        if let Some(control) = &self.socket_control {\n            control.deregister();\n        }\n\n        log::debug!(\"Close process completed\");\n\n        task_result\n    }\n\n    async fn close_stream_task(&self, timeout: Duration) -> Result<(), Error> {\n        match self.handler_tasks.finish_shutdown(timeout, timeout).await {\n            Ok(()) => Ok(()),\n            Err(error @ TaskShutdownError::Timeout { .. }) => Err(Error::Io(std::io::Error::new(\n                std::io::ErrorKind::TimedOut,\n                format!(\"Timed out joining WebSocket handler task after abort: {error}\"),\n            ))),\n            Err(e) => Err(Error::Io(std::io::Error::other(format!(\n                \"WebSocket handler shutdown failed: {e}\"\n            )))),\n        }\n    }\n\n    /// Get active subscriptions for a specific instrument.\n    pub fn get_subscriptions(&self, instrument_id: InstrumentId) -> Vec<OKXWsChannel> {\n        let symbol = instrument_id.symbol.inner();\n        let mut channels = Vec::new();\n\n        for entry in self.subscriptions_inst_id.iter() {\n            let (channel, instruments) = entry.pair();\n            if instruments.contains(&symbol) {\n                channels.push(channel.clone());","sourceCodeStart":1042,"sourceCodeEnd":1078,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/okx/src/websocket/client.rs#L1042-L1078","documentation":"When closing a WebSocket stream (during connect rollback, close_locked, or a timeout test path), the OKX client aborts its handler task and waits `timeout` for it to finish via finish_shutdown. If the task does not join within the timeout, an io::Error of kind TimedOut is raised: the handler task is stuck and shutdown is incomplete, risking leaked tasks or sockets.","triggerScenarios":"Calling connect (which rolls back via close_stream_task on failure), close_locked during disconnect, or otherwise aborting a WebSocket handler task whose read loop or message pump is blocked (e.g. blocked on a synchronous send into a full channel, or a lock held elsewhere).","commonSituations":"Underlying TCP connection stalled without triggering read timeout so the reader task never wakes; a consumer of the handler's output channel stopped reading (backpressure); deadlock with another lock; overly short shutdown timeout configured.","solutions":["Increase the shutdown timeout if it is shorter than the handler's worst-case wake-up latency.","Ensure all consumers of the handler task's channels keep draining so it can observe the abort and exit.","Check for lock contention or blocking calls inside the handler loop that prevent reacting to cancellation.","Enable TCP keepalive / read timeouts on the socket so a dead connection actually errors out and unblocks the reader.","If it occurs during connect failure, inspect the original connect error; this timeout is a secondary symptom."],"exampleFix":"// before\nclient.close_stream_task(Duration::from_millis(50)).await?;\n// after\nclient.close_stream_task(Duration::from_secs(5)).await?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.close_stream_task(Duration::from_secs(5)).await {\n    Ok(()) => {}\n    Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {\n        log::error!(\"handler task did not join: {e}; check channel consumers/locks\");\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Always drain channels the handler task writes to so it can exit on abort","Avoid blocking calls or long-held locks inside WS handler loops","Set socket read/keepalive timeouts so stalled connections wake the reader","Size the shutdown timeout to exceed worst-case handler wake latency"],"tags":["okx","websocket","task-shutdown","timeout","async"],"backgroundTag":"request-timeout","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}