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 theView on GitHub (pinned to 18893faf8b)
Solutions
- Read the inner {error} in the message, which names the real handler failure, and fix that root cause first.
- Inspect logs for panics or WebSocket errors in the handler task and harden that path (avoid unwrap on exchange payloads).
- Recreate the client after this error; the handler is dead and reservations were released.
- 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
- Fix the inner handler error first; this message only wraps it.
- Harden handler code against panics (no unwrap on exchange payloads).
- Make best-effort disconnects tolerant: log and rebuild rather than fail teardown.
- Watch for network drops that kill the handler before you call disconnect().
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
- Hyperliquid WebSocket handler did not stop after abort
- failed to finish Binance Futures data command tasks: {e}
- failed to finish Binance Spot data command tasks: {e}
- Failed to start Hyperliquid WebSocket handler task: {e}
- Polymarket RTDS task shutdown failed: {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/0d75aa27f541b548.
Report an issue: GitHub.