{"record":{"id":"7b8c1da04c1e3a03","repo":"nautechsystems/nautilus_trader","slug":"coinbase-websocket-handler-did-not-stop-after-abor","errorCode":null,"errorMessage":"Coinbase WebSocket handler did not stop after abort","messagePattern":"Coinbase WebSocket handler did not stop after abort","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/coinbase/src/websocket/client.rs","lineNumber":241,"sourceCode":"        if self.is_active() || self.is_reconnecting() {\n            log::warn!(\"WebSocket already connected or reconnecting\");\n            return Ok(());\n        }\n\n        if let Some(outcome) = finish_task(\n            &mut self.task_handle,\n            WS_DISCONNECT_TIMEOUT,\n            WS_DISCONNECT_TIMEOUT,\n        )\n        .await\n        {\n            match outcome {\n                TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted => {}\n                TaskJoinOutcome::Failed(error) => {\n                    anyhow::bail!(\"Coinbase WebSocket handler failed: {error}\");\n                }\n                TaskJoinOutcome::Incomplete => {\n                    anyhow::bail!(\"Coinbase WebSocket handler did not stop after abort\");\n                }\n            }\n        }\n\n        // Clear stop signal from any previous disconnect\n        self.signal.store(false, Ordering::Relaxed);\n\n        let (message_handler, raw_rx) = channel_message_handler();\n        let cfg = WebSocketConfig {\n            url: self.url.clone(),\n            headers: vec![],\n            // Coinbase uses TCP control-frame pings for transport keep-alive;\n            // application-layer liveness comes from the heartbeats channel.\n            heartbeat_interval_secs: Some(WS_HEARTBEAT_SECS),\n            heartbeat_payload: None,\n            connect_timeout_ms: Some(RECONNECT_TIMEOUT.as_millis() as u64),\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),","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/coinbase/src/websocket/client.rs#L223-L259","documentation":"When closing/reconnecting, connect() aborts the previous handler task and expects the task join to report Completed or Aborted. If the outcome is Incomplete (task neither finished nor confirmed aborted), the adapter treats this as an internal lifecycle violation and bails.","triggerScenarios":"The handler task ignored the abort signal and the join result came back Incomplete — typically a stuck handler loop not honoring the stop signal or a join race during rapid disconnect/reconnect cycles.","commonSituations":"Handler blocked on a non-cancellation-safe await (e.g. a lock or a send that never completes) while disconnect() aborts it; calling connect() concurrently from multiple tasks.","solutions":["Avoid concurrent connect()/disconnect() calls; serialize connection lifecycle on one task","Retry the connect — this is usually a transient lifecycle race","If persistent, inspect the handler loop to ensure it selects on the stop signal and report/patch the adapter"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in 0..3 {\n    match ws.connect().await {\n        Ok(_) => break,\n        Err(e) if e.to_string().contains(\"did not stop after abort\") => {\n            tokio::time::sleep(Duration::from_millis(200 * (attempt + 1))).await;\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Never call connect()/disconnect() concurrently from multiple tasks","Own the WebSocket lifecycle in a single supervisor task","Avoid rapid disconnect/reconnect loops"],"tags":["rust","coinbase","websocket","task-lifecycle"],"backgroundTag":"internal-invariant-violation","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"}