nautechsystems/nautilus_trader · error · anyhow::Error

Failed to send Unsubscribe command: {e}

Error message

Failed to send Unsubscribe command: {e}

What it means

`unsubscribe` sends an Unsubscribe command to the handler task over the mpsc command channel; failure means the receiver no longer exists, i.e. the handler task has terminated. The SendError is wrapped with this message.

Source

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

        let channel_str = channel.as_ref();

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

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

    /// Returns the next parsed message from the feed handler.
    pub async fn next_message(&mut self) -> Option<NautilusWsMessage> {
        self.out_rx.as_mut()?.recv().await
    }

    /// Disconnects the WebSocket and stops the feed handler.
    pub(crate) fn begin_shutdown(&self) {
        self.signal.store(true, Ordering::Release);
    }

    /// Disconnects the WebSocket and stops the feed handler.
    pub async fn disconnect(&mut self) -> anyhow::Result<()> {
        // Send Disconnect command before setting the signal so the handler
        // processes it and calls notify_closed() on the inner WebSocket client
        let cmd_tx = self.cmd_tx.read().await;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the client is still connected/running before unsubscribing
  2. If the handler is gone, the subscriptions are already gone — treat this as a no-op rather than an unrecoverable fault
  3. Reconnect the client before attempting further subscribe/unsubscribe calls
  4. Ensure close() is not called before pending unsubscribe calls complete

Example fix

// before
client.unsubscribe(channel, &products).await?;
// after
if let Err(e) = client.unsubscribe(channel, &products).await {
    log::debug!("unsubscribe skipped, client closed: {e}");
}
Defensive patterns

Strategy: try-catch

Validate before calling

async fn safe_unsubscribe(client: &CoinbaseWsClient, channel: CoinbaseWsChannel, products: &[Ustr]) {
    if client.is_closed() { return; } // subscriptions are already gone
    let _ = client.unsubscribe(channel, products).await;
}

Try / catch

if let Err(e) = client.unsubscribe(channel, &products).await {
    log::debug!("unsubscribe ignored (handler gone): {e}");
}

Prevention

When it happens

Trigger: Calling `unsubscribe` after the websocket handler task has exited (client closed, connection terminated, or task crashed), so `cmd_tx.send(HandlerCommand::Unsubscribe)` fails.

Common situations: Tearing down subscriptions during shutdown after the client was already closed; unsubscribe racing with a fatal disconnect.

Related errors


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