{"record":{"id":"5cc72778f245f305","repo":"nautechsystems/nautilus_trader","slug":"lighter-websocket-handler-did-not-stop-after-abort","errorCode":null,"errorMessage":"Lighter WebSocket handler did not stop after abort","messagePattern":"Lighter WebSocket handler did not stop after abort","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/websocket/client.rs","lineNumber":442,"sourceCode":"        if self.is_active() {\n            log::warn!(\"Lighter WebSocket already connected\");\n            return Ok(());\n        }\n\n        if let Some(outcome) = finish_task(\n            &mut self.task_handle,\n            DISCONNECT_TIMEOUT,\n            DISCONNECT_TIMEOUT,\n        )\n        .await\n        {\n            match outcome {\n                TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted => {}\n                TaskJoinOutcome::Failed(error) => {\n                    anyhow::bail!(\"Lighter WebSocket handler failed: {error}\");\n                }\n                TaskJoinOutcome::Incomplete => {\n                    anyhow::bail!(\"Lighter WebSocket handler did not stop after abort\");\n                }\n            }\n        }\n\n        self.signal.store(false, Ordering::Release);\n        self.initial_connect_cancellation\n            .store(Arc::new(cancellation_token.clone()));\n\n        let (message_handler, raw_rx) = channel_epoch_message_handler();\n        let cfg = WebSocketConfig {\n            url: self.url.clone(),\n            headers: vec![],\n            heartbeat_interval_secs: Some(HEARTBEAT_INTERVAL.as_secs()),\n            heartbeat_payload: None,\n            connect_timeout_ms: Some(self.ws_timeout_secs.saturating_mul(1_000).max(1)),\n            reconnect_delay_initial_ms: Some(RECONNECT_BASE_BACKOFF.as_millis() as u64),\n            reconnect_delay_max_ms: Some(RECONNECT_MAX_BACKOFF.as_millis() as u64),\n            reconnect_backoff_factor: Some(RECONNECT_BACKOFF_FACTOR),","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/websocket/client.rs#L424-L460","documentation":"Raised by connect_with_cancellation when the WebSocket handler task neither completed, aborted cleanly, nor failed after an abort signal was sent — the join returned TaskJoinOutcome::Incomplete. The library treats a handler that ignores abort as a hard startup failure and bails instead of returning a half-connected client.","triggerScenarios":"Calling connect() when the spawned handler task does not terminate in response to the abort signal during initial connection setup — e.g. the task is stuck awaiting a socket operation that does not observe cancellation.","commonSituations":"A wedged TCP connection to Lighter where the handler's select loop does not poll the abort token; pathological network conditions (black-holed connections); a bug or very old version of the underlying websocket transport that ignores cancellation.","solutions":["Check network path to the Lighter WebSocket endpoint — a hung/unresponsive connection is the usual cause; fix connectivity or timeout settings.","Increase ws_timeout_secs if the endpoint is slow, so the handler has more time before the join/abort logic runs.","Restart the data client / node; this error usually means a stuck task that won't recover on its own.","Upgrade the adapter/transport crates in case of a known cancellation-handling bug.","Report with debug logs if reproducible — a handler that ignores abort indicates a task-loop bug."],"exampleFix":"// before: default timeout too tight for a slow endpoint\nlet client = LighterWsClient::connect(..., /*ws_timeout_secs*/ 5).await?;\n\n// after: allow more time before the abort/join path is exercised\nlet client = LighterWsClient::connect(..., /*ws_timeout_secs*/ 30).await?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.connect().await {\n    Err(e) if e.to_string().contains(\"did not stop after abort\") => {\n        log::error!(\"handler wedged; restarting client\");\n        client = build_ws_client(cfg)?; // rebuild rather than reuse\n        client.connect().await?;\n    }\n    r => r?,\n}","preventionTips":["Use sane ws_timeout_secs values so abort has time to work","Avoid endpoints behind flaky proxies that hang connections","Rebuild the client instead of reconnecting after this error","Report persistent occurrences — indicates a handler loop bug"],"tags":["websocket","async","rust","timeout"],"backgroundTag":"request-timeout","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}