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

  1. During shutdown this is benign — log and continue; the connection is already closed.
  2. Keep the handler task alive until all unsubscriptions complete; fix drop ordering.
  3. If subscriptions must be managed afterwards, re-create/reconnect the client.
  4. 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

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


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