nautechsystems/nautilus_trader · error · anyhow::Error

Hyperliquid WebSocket handler failed: {error}

Error message

Hyperliquid WebSocket handler failed: {error}

What it means

Raised in disconnect_locked after the client aborts the WebSocket handler task and joins it: if the join yields TaskJoinOutcome::Failed(error), the handler task terminated with an error rather than completing or aborting cleanly. The client releases its rate-limit reservations and propagates the handler's underlying error wrapped in this message, so the real cause is in the inner error text.

Source

Thrown at crates/adapters/hyperliquid/src/websocket/client.rs:582

                "Failed to send disconnect command (handler may already be shut down): {e}"
            );
        }

        if self.task_handle.is_empty() {
            log::debug!("No task handle to await");
        } else {
            log::debug!("Waiting for task handle to complete");

            if let Some(outcome) = self
                .task_handle
                .finish(Duration::from_secs(2), Duration::from_secs(2))
                .await
            {
                match outcome {
                    TaskJoinOutcome::Completed(()) | TaskJoinOutcome::Aborted => {}
                    TaskJoinOutcome::Failed(error) => {
                        self.release_limit_reservations();
                        anyhow::bail!("Hyperliquid WebSocket handler failed: {error}");
                    }
                    TaskJoinOutcome::Incomplete => {
                        self.release_limit_reservations();
                        anyhow::bail!("Hyperliquid WebSocket handler did not stop after abort");
                    }
                }
            }
        }
        self.release_limit_reservations();
        log::debug!("Disconnected");
        Ok(())
    }

    /// Requests a full transport reconnect.
    ///
    /// Transitions the connection from `Active` to `Reconnect`; the network
    /// layer re-establishes the socket with backoff and the handler replays all
    /// active subscriptions once reconnected. Returns `false` when the

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the inner {error} in the message, which names the real handler failure, and fix that root cause first.
  2. Inspect logs for panics or WebSocket errors in the handler task and harden that path (avoid unwrap on exchange payloads).
  3. Recreate the client after this error; the handler is dead and reservations were released.
  4. During best-effort teardown, treat this error as non-fatal since the connection was already broken.

Example fix

// before: propagate handler failure during shutdown teardown / client.disconnect().await?; / // after: best-effort teardown / if let Err(e) = client.disconnect().await { log::warn!("disconnect after handler failure (ignoring): {e}"); }
Defensive patterns

Strategy: try-catch

Try / catch

if let Err(e) = client.disconnect().await { if e.to_string().contains("handler failed") { log::warn!("handler already failed during disconnect: {e}"); client = HyperliquidWebSocketClient::new(url, ...); } }

Prevention

When it happens

Trigger: Calling disconnect() (or the reconnect path in connect_locked) while the handler task has already failed with an error: the WebSocket connection errored, a command failed, or the handler panicked and the join captured the payload.

Common situations: Network drop or exchange-side WebSocket close killing the handler just before disconnect; panics inside message-handling code (unwrap on unexpected payloads); calling disconnect() concurrently with a failing connection.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/0d75aa27f541b548. Report an issue: GitHub.