nautechsystems/nautilus_trader · error · anyhow::Error

Failed to send Subscribe command: {e}

Error message

Failed to send Subscribe command: {e}

What it means

`subscribe` dispatches the subscription to the internal handler task over an mpsc command channel. If the send fails (the receiver/handler task has already terminated), the error is wrapped as 'Failed to send Subscribe command'. This indicates the client's background handler is no longer running.

Source

Thrown at crates/adapters/coinbase/src/websocket/client.rs:424

        let channel_str = channel.as_ref();

        if product_ids.is_empty() {
            self.subscriptions.mark_subscribe(channel_str);
        } else {
            for product_id in product_ids {
                let topic = format!("{channel_str}|{product_id}");
                self.subscriptions.mark_subscribe(&topic);
            }
        }

        let cmd_tx = self.cmd_tx.read().await;
        cmd_tx
            .send(HandlerCommand::Subscribe {
                channel: sub.channel,
                product_ids: sub.product_ids,
                payload: sub.payload,
            })
            .map_err(|e| anyhow::anyhow!("Failed to send Subscribe command: {e}"))
    }

    /// Unsubscribes from a channel for the given product IDs.
    pub async fn unsubscribe(
        &self,
        channel: CoinbaseWsChannel,
        product_ids: &[Ustr],
    ) -> anyhow::Result<()> {
        let jwt = self.credential.as_ref().and_then(|c| c.build_ws_jwt().ok());

        let unsub = protect_subscription(CoinbaseWsSubscription {
            msg_type: CoinbaseWsAction::Unsubscribe,
            product_ids: product_ids.to_vec(),
            channel,
            jwt,
        })?;

        let channel_str = channel.as_ref();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check that the client/handler task is still running before subscribing; recreate the client if it was shut down
  2. Retry connect() to restart the task generation, then resubscribe
  3. Inspect logs for handler task panics or disconnects preceding the error
  4. Guard the shutdown path so subscribe is not called concurrently with close

Example fix

// before
client.subscribe(channel, &products).await?;
// after
if client.is_closed() {
    client.reconnect().await?;
}
client.subscribe(channel, &products).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

async fn ensure_active(client: &CoinbaseWsClient) -> Result<(), anyhow::Error> {
    if client.is_closed() {
        anyhow::bail!("websocket client already closed; reconnect before subscribing");
    }
    Ok(())
}

Try / catch

match client.subscribe(channel, &products).await {
    Err(e) if e.to_string().contains("Failed to send Subscribe command") => {
        client.reconnect().await?;
        client.subscribe(channel, &products).await?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `subscribe` after the websocket handler task exited (connection dropped permanently, client shut down, or the task panicked), so `cmd_tx.send(HandlerCommand::Subscribe)` returns a SendError.

Common situations: Reusing a client after `close`/drop of the handler, subscribe racing with shutdown during app teardown, or handler task crash on malformed state.

Related errors


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