{"record":{"id":"24c726e1db40fced","repo":"nautechsystems/nautilus_trader","slug":"lighter-websocket-initial-connection-cancelled","errorCode":null,"errorMessage":"Lighter WebSocket initial connection cancelled","messagePattern":"Lighter WebSocket initial connection cancelled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/adapters/lighter/src/websocket/client.rs","lineNumber":498,"sourceCode":"                    .map(SocketControl::sink)\n                    .or_else(|| self.socket_sink.clone()),\n            )\n            .connect();\n        let client =\n            match tokio::time::timeout(Duration::from_secs(self.ws_timeout_secs), connect).await {\n                Ok(result) => result?,\n                Err(_) => anyhow::bail!(\n                    \"Lighter WebSocket initial connection timeout after {} seconds\",\n                    self.ws_timeout_secs,\n                ),\n            };\n\n        if cancellation_token.is_cancelled()\n            || generation != self.connection_generation.load(Ordering::Acquire)\n        {\n            client.disconnect().await;\n\n            anyhow::bail!(\"Lighter WebSocket initial connection cancelled\");\n        }\n\n        let (cmd_tx, cmd_rx) = tokio::sync::mpsc::unbounded_channel::<HandlerCommand>();\n        let (out_tx, out_rx) = tokio::sync::mpsc::unbounded_channel::<NautilusWsMessage>();\n\n        // Capture the connection-mode atomic before moving `client` into the\n        // SetClient command below.\n        let connection_mode_atomic = client.connection_mode_atomic();\n        let connection_epoch_atomic = client.connection_epoch_atomic();\n\n        // Queue SetClient (and the instrument cache replay) onto the new\n        // command channel BEFORE publishing it to clones or marking the\n        // connection active. Otherwise a clone observing `is_active()` could\n        // race in and send a Subscribe before SetClient lands, and the\n        // handler would drop the subscription because `inner == None`.\n        let reconnect_handle = client.reconnect_handle();\n        if let Err(e) = cmd_tx.send(HandlerCommand::SetClient(client)) {\n            anyhow::bail!(\"Failed to send SetClient command: {e}\");","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/websocket/client.rs#L480-L516","documentation":"Raised when the initial Lighter WebSocket connection succeeded, but by the time it finished the cancellation token was cancelled or the connection generation advanced (a newer connect attempt superseded this one). The client is disconnected and the stale attempt is aborted with this error instead of returning a connection that nobody owns.","triggerScenarios":"Calling connect() and cancelling the CancellationToken while the handshake is in flight, or calling connect()/reconnect concurrently such that connection_generation increments before the first attempt finishes — the losing attempt bails here.","commonSituations":"Shutting down a node or data engine while the WebSocket client is still connecting; rapid disconnect/reconnect cycles (e.g. stream URL hot-reload) racing an in-progress connect; a caller dropping the connect future and retrying.","solutions":["If intentional shutdown: this is expected; handle the error as a benign cancellation rather than a fault.","Avoid concurrent connect() calls on the same client; await the first connect before triggering a reconnect.","Check whether something cancels the token early (e.g. a shutdown signal firing during startup) and fix the shutdown ordering.","If reconnect storms occur, add backoff between reconnect attempts so generations don't race.","Upgrade the adapter if a known race in generation handling causes spurious cancellations."],"exampleFix":"// before: cancelling while connect is in flight\nlet handle = tokio::spawn(client.connect());\ntoken.cancel(); // connect will bail: initial connection cancelled\nlet client = handle.await??;\n\n// after: connect first, then allow cancellation\nclient.connect().await?;\ntoken.cancel(); // now shutdown is orderly","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match client.connect().await {\n    Err(e) if e.to_string().contains(\"initial connection cancelled\") => {\n        log::info!(\"connect superseded or cancelled; treating as benign\");\n    }\n    r => r?,\n}","preventionTips":["Don't cancel the token while connect() is in flight unless shutting down","Serialize connect/reconnect calls; await one before triggering another","Check shutdown ordering so tokens aren't cancelled during startup","Treat this error as benign during intentional shutdown"],"tags":["websocket","async","cancellation","race-condition"],"backgroundTag":"invalid-state-transition","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"}