{"record":{"id":"b49e77b98d7345a1","repo":"nautechsystems/nautilus_trader","slug":"lighter-websocket-did-not-reach-active-state","errorCode":null,"errorMessage":"Lighter WebSocket did not reach active state","messagePattern":"Lighter WebSocket did not reach active state","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/data/mod.rs","lineNumber":419,"sourceCode":"            .context(\"failed to connect to Lighter WebSocket\")?;\n\n        if let Err(e) = ws_guard.client_mut().wait_until_active().await {\n            let ws_client = ws_guard.disarm();\n            let mut rollback_errors = Vec::new();\n\n            if let Err(e) = ws_client\n                .disconnect_with_task_retention(Arc::clone(&self.ws_handler_retained))\n                .await\n            {\n                rollback_errors.push(e.to_string());\n            }\n\n            if let Err(e) = self.ws_handler_retained.finish().await {\n                rollback_errors.push(e.to_string());\n            }\n\n            let readiness_error =\n                anyhow::Error::new(e).context(\"Lighter WebSocket did not reach active state\");\n\n            if rollback_errors.is_empty() {\n                return Err(readiness_error);\n            }\n            return Err(readiness_error.context(format!(\n                \"Lighter WebSocket readiness rollback failed: {}\",\n                rollback_errors.join(\"; \")\n            )));\n        }\n\n        let mut ws_client = ws_guard.disarm();\n        self.ws_client.set_task_slot(ws_client.take_task_slot());\n\n        let cancellation_token = self.cancellation_token.clone();\n        let data_sender = self.data_sender.clone();\n        let market_stats_subscriptions = Arc::clone(&self.market_stats_subscriptions);\n\n        let future = async move {","sourceCodeStart":401,"sourceCodeEnd":437,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/data/mod.rs#L401-L437","documentation":"LighterMarketDataClient::spawn_ws connects the venue WebSocket and then waits for it to reach 'active' state via wait_until_active(). If the wait errors (handshake accepted but the socket never becomes active, subscription/auth rejection, or connection dropped), the client rolls back (disconnects, finishes the retained handler) and returns the readiness error, optionally wrapped with rollback failure details.","triggerScenarios":"connect_with_cancellation succeeds but wait_until_active() returns Err — e.g. WS closed before active, auth failure on connect, or server never sends the active/ready signal; possibly also if cancellation token is cancelled mid-wait.","commonSituations":"Lighter WS endpoint outage or maintenance, wrong network (mainnet vs testnet) URL, firewall/proxy dropping long-lived WebSockets, or an API key lacking data permissions.","solutions":["Read the wrapped inner error from wait_until_active() to see if it was a close, auth, or timeout.","Verify the WebSocket URL points to the correct Lighter environment (mainnet/testnet).","Retry connect after a delay; transient readiness failures are common during venue maintenance.","Check network/proxy settings that may terminate WebSocket upgrades; confirm cancellation token is not being cancelled early."],"exampleFix":"// before\nclient.connect().await?; // assumes WS will go active\n// after\nmatch client.connect().await { Err(e) if is_readiness(&e) => { tokio::time::sleep(RETRY_DELAY).await; client.connect().await } r => r }","handlingStrategy":"retry","validationCode":"// Rust: check config before connect\nassert!(!ws_url.is_empty());\nassert!(ws_url.starts_with(\"wss://\"));","typeGuard":null,"tryCatchPattern":"match client.connect().await {\n    Err(e) if format!(\"{e:#}\").contains(\"did not reach active state\") => {\n        tokio::time::sleep(Duration::from_secs(2)).await;\n        client.connect().await?; // bounded retries\n    }\n    r => r?,\n}","preventionTips":["Confirm the Lighter WS URL matches the intended network (mainnet/testnet)","Add bounded reconnect with exponential backoff","Check for proxies/firewalls that kill idle WebSockets","Avoid cancelling the cancellation token during connect"],"tags":["rust","lighter","websocket","connectivity"],"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"}