nautechsystems/nautilus_trader · error · anyhow::Error
Failed to send RemoveBarType command: {e}
Error message
Failed to send RemoveBarType command: {e} What it means
This error wraps a failed send of RemoveBarType on the internal mpsc command channel while unsubscribing from bars. The client removes the bar key from its local registry and notifies the handler so it can stop routing bar data; failure means the handler task's receiver is closed. The unsubscribe cannot complete because the client is already shut down.
Source
Thrown at crates/adapters/hyperliquid/src/websocket/client.rs:1942
/// Unsubscribe from candle/bar data.
pub async fn unsubscribe_bars(&self, bar_type: BarType) -> anyhow::Result<()> {
let instrument_id = bar_type.instrument_id();
let instrument = self
.get_instrument(&instrument_id)
.ok_or_else(|| InstrumentLookupError::not_found(instrument_id))?;
let coin = instrument.raw_symbol().inner();
let interval = bar_type_to_interval(&bar_type)?;
let subscription = SubscriptionRequest::Candle { coin, interval };
let key = format!("candle:{coin}:{interval}");
self.bar_types.remove(&key);
let cmd_tx = self.cmd_tx.read().await;
cmd_tx
.send(HandlerCommand::RemoveBarType { key })
.map_err(|e| anyhow::anyhow!("Failed to send RemoveBarType command: {e}"))?;
self.send_unsubscription(&cmd_tx, subscription)?;
Ok(())
}
/// Unsubscribe from funding rate updates for an instrument.
pub async fn unsubscribe_funding_rates(
&self,
instrument_id: InstrumentId,
) -> anyhow::Result<()> {
self.unsubscribe_asset_context_data(instrument_id, AssetContextDataType::FundingRate)
.await
}
/// Unsubscribe from open interest updates for an instrument.
pub async fn unsubscribe_open_interest(
&self,
instrument_id: InstrumentId,View on GitHub (pinned to 18893faf8b)
Solutions
- During shutdown this is benign — log and continue; the connection is already closed.
- Keep the handler task alive until all unsubscriptions complete; fix drop ordering.
- If subscriptions must be managed afterwards, re-create/reconnect the client.
- Check handler-task logs for the reason the receiver loop ended.
Example fix
// before client.disconnect().await?; client.unsubscribe_bars(bar_type).await?; // channel closed // after client.unsubscribe_bars(bar_type).await?; client.disconnect().await?;
Defensive patterns
Strategy: try-catch
Validate before calling
// rust
if !client.is_connected() {
// already torn down; bar routing is gone anyway
return Ok(());
} Try / catch
// rust
if let Err(e) = client.unsubscribe_bars(bar_type).await {
if e.to_string().contains("RemoveBarType") && shutting_down {
tracing::debug!("benign bar unsubscribe after shutdown: {e}");
} else { return Err(e); }
} Prevention
- Unsubscribe bars before disconnect(); never the reverse.
- Keep handler task alive until teardown completes (check drop order).
- Filter closed-channel errors from metrics during shutdown to avoid noise.
- After reconnects, use the fresh client for both subscribe and unsubscribe.
When it happens
Trigger: Calling unsubscribe_bars after disconnect(), a handler panic, or drop/cancellation of the handler task; also when runtime shutdown races the unsubscribe call.
Common situations: Teardown-order bugs where the handler task is dropped before stream unsubscription; stale client reused after reconnect; shutdown handlers calling unsubscribe after disconnect.
Related errors
- Failed to send SetDepth10Sub command: {e}
- Failed to send UpdateTradeSubs command: {e}
- Failed to send AddBarType command: {e}
- Failed to send resubscribe command: {e}
- Failed to send subscribe command: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/eb156fe121b4a7df.
Report an issue: GitHub.