nautechsystems/nautilus_trader · error · anyhow::Error

Failed to send UpdateAssetContextSubs command: {e}

Error message

Failed to send UpdateAssetContextSubs command: {e}

What it means

This error is raised when the Hyperliquid WebSocket client cannot forward an UpdateAssetContextSubs HandlerCommand to its internal handler task over the command channel. It means the command mpsc channel is closed or the receiver was dropped, i.e. the handler task is no longer running. It is a wrapper around the underlying SendError, not a Hyperliquid exchange error.

Source

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

        instrument_id: InstrumentId,
        data_type: AssetContextDataType,
    ) -> anyhow::Result<()> {
        let instrument = self
            .get_instrument(&instrument_id)
            .ok_or_else(|| InstrumentLookupError::not_found(instrument_id))?;
        let coin = instrument.raw_symbol().inner();

        let mut entry = self.asset_context_subs.entry(coin).or_default();
        let is_first_subscription = entry.is_empty();
        entry.insert(data_type);
        let data_types = entry.clone();
        drop(entry);

        let cmd_tx = self.cmd_tx.read().await;

        cmd_tx
            .send(HandlerCommand::UpdateAssetContextSubs { coin, data_types })
            .map_err(|e| anyhow::anyhow!("Failed to send UpdateAssetContextSubs command: {e}"))?;

        if is_first_subscription {
            log::debug!(
                "First asset context subscription for coin '{coin}', subscribing to ActiveAssetCtx"
            );
            let subscription = SubscriptionRequest::ActiveAssetCtx { coin };

            cmd_tx
                .send(HandlerCommand::UpdateInstrument(instrument.clone()))
                .map_err(|e| anyhow::anyhow!("Failed to send UpdateInstrument command: {e}"))?;

            if let Err(e) = self.send_subscription(&cmd_tx, subscription) {
                if let Some(mut entry) = self.asset_context_subs.get_mut(&coin) {
                    entry.remove(&data_type);
                    let rollback = entry.clone();
                    let remove_entry = entry.is_empty();
                    drop(entry);

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Reconnect the WebSocket client before subscribing; check connection state first
  2. Verify the client was connected and started before calling subscribe
  3. Treat this as a terminal client state: recreate the client instance
  4. Inspect logs for a prior handler-task panic that closed the command channel

Example fix

// before
client.subscribe(data_type).await?;
// after
if !client.is_connected() {
    client.connect().await?;
}
client.subscribe(data_type).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust
fn can_subscribe(client: &HyperliquidWsClient) -> bool {
    client.is_connected()
}

Try / catch

match client.subscribe(data_type).await {
    Ok(()) => {},
    Err(e) if e.to_string().contains("Failed to send") => {
        client.connect().await?;
        client.subscribe(data_type).await?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling subscribe (or the asset-context subscription path) after the WebSocket handler task has terminated, e.g. after disconnect without reconnect, or calling before the client's handler task was ever started. cmd_tx.send returns Err and the anyhow wrapper adds this message.

Common situations: Reusing a client after connection teardown; a race where the handler task crashed (panic or network shutdown) while subscription logic still runs; calling subscribe concurrently with close(); keeping a stale client handle in long-lived services.

Related errors


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