{"record":{"id":"67e21d41a6d2ce16","repo":"nautechsystems/nautilus_trader","slug":"failed-to-start-hyperliquid-websocket-handler-task","errorCode":null,"errorMessage":"Failed to start Hyperliquid WebSocket handler task: {e}","messagePattern":"Failed to start Hyperliquid WebSocket handler task: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/hyperliquid/src/websocket/client.rs","lineNumber":507,"sourceCode":"                        }\n                    }\n                    None => {\n                        if handler.is_stopped() {\n                            log::debug!(\"Stop signal received, ending message processing\");\n                            break;\n                        }\n                        log::warn!(\"WebSocket stream ended unexpectedly\");\n                        break;\n                    }\n                }\n            }\n            rate_limits.release_client(client_id);\n            connection_permit.lock().take();\n            log::debug!(\"Handler task completed\");\n        }) {\n            self.out_rx = None;\n            self.release_limit_reservations();\n            anyhow::bail!(\"Failed to start Hyperliquid WebSocket handler task: {e}\");\n        }\n        Ok(())\n    }\n\n    pub fn set_post_timeout(&mut self, timeout: Duration) {\n        self.post_timeout = timeout;\n    }\n\n    pub(crate) fn begin_shutdown(&self) {\n        self.signal.store(true, Ordering::Relaxed);\n    }\n\n    /// Replaces state owned by a terminated WebSocket generation.\n    ///\n    /// This must run only after the handler task has stopped. Replacing the\n    /// shared containers, rather than clearing them, prevents old clones or\n    /// in-flight work from mutating a subsequent connection generation.\n    pub(crate) fn reset_runtime_state(&mut self) {","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/hyperliquid/src/websocket/client.rs#L489-L525","documentation":"Raised in connect_locked when tokio::spawn of the WebSocket handler task fails. The spawn failure means the tokio runtime refused to start the task, most commonly because the runtime is shutting down, so no handler exists to process commands or read the WebSocket and the connection attempt is aborted. The client resets out_rx and releases reserved rate-limiter slots before returning.","triggerScenarios":"Calling connect() (via connect_locked) while the enclosing tokio runtime is shutting down or has already been dropped, so tokio::spawn cannot register the new handler task.","commonSituations":"Calling connect() from a Drop impl or shutdown hook; a reconnect loop still running while the runtime tears down; dropping the Runtime/Handle while the client lives; blocking a runtime until shutdown while retries continue.","solutions":["Ensure connect() is only awaited while the tokio runtime is alive; move reconnection logic into a normally spawned task.","Check whether the process/runtime is shutting down and stop reconnect attempts when a shutdown signal is observed.","Keep the Runtime/Handle alive for the client's whole lifetime and shut it down only after dropping clients.","Retry connect() on a live runtime if the failure was transient shutdown overlap."],"exampleFix":"// before: connect from Drop while runtime shuts down / impl Drop for MyService { fn drop(&mut self) { rt.block_on(client.connect()); } } / // after: connect inside a live runtime task / async fn run(service: MyService) -> anyhow::Result<()> { service.client.connect().await?; service.run().await }","handlingStrategy":"retry","validationCode":"fn can_connect(shutdown_requested: bool) -> bool { !shutdown_requested }","typeGuard":null,"tryCatchPattern":"match client.connect().await { Err(e) if e.to_string().contains(\"handler task\") && shutdown_requested() => { log::info!(\"skipping reconnect: runtime shutting down\"); } Err(e) => return Err(e.into()), Ok(()) => {} }","preventionTips":["Never call connect() from Drop impls or shutdown hooks.","Hold the tokio Runtime/Handle for the client's entire lifetime.","Observe shutdown signals and cancel reconnect loops before teardown.","Keep reconnect logic inside a normally spawned task on the live runtime."],"tags":["websocket","tokio","async","runtime","task-spawn"],"backgroundTag":"network-request-failed","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"}