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
- Verify the client is still connected/running before unsubscribing
- If the handler is gone, the subscriptions are already gone — treat this as a no-op rather than an unrecoverable fault
- Reconnect the client before attempting further subscribe/unsubscribe calls
- 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
- Skip unsubscribe on already-closed clients
- Don't interleave unsubscribe calls with client shutdown
- Treat unsubscribe failure during teardown as informational, not fatal
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
- Failed to send Subscribe command: {e}
- Failed to send SetClient command: {e}
- WS handler command sender unavailable
- std::mem::take(&mut self.shutdown_errors).join("; ")
- Binance Spot public JSON stream pool is shutting down
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a25b749bd12c3eb7.
Report an issue: GitHub.