nautechsystems/nautilus_trader · error · anyhow::Error

Failed to send AddBarType command: {e}

Error message

Failed to send AddBarType command: {e}

What it means

This error wraps a failed send of AddBarType on the internal mpsc command channel during bar subscription. After registering the instrument, the client sends the BarType key to the handler so incoming bar data is routed; failure means the handler task's receiver is closed. The client rolls back its local bar_types registry before returning this error.

Source

Thrown at crates/adapters/hyperliquid/src/websocket/client.rs:1602

        let interval = bar_type_to_interval(&bar_type)?;
        let subscription = SubscriptionRequest::Candle { coin, interval };

        // Cache the bar type for parsing using canonical key
        let key = format!("candle:{coin}:{interval}");
        self.bar_types.insert(key.clone(), bar_type);

        let cmd_tx = self.cmd_tx.read().await;

        cmd_tx
            .send(HandlerCommand::UpdateInstrument(instrument.clone()))
            .map_err(|e| anyhow::anyhow!("Failed to send UpdateInstrument command: {e}"))?;

        cmd_tx
            .send(HandlerCommand::AddBarType {
                key: key.clone(),
                bar_type,
            })
            .map_err(|e| anyhow::anyhow!("Failed to send AddBarType command: {e}"))?;

        if let Err(e) = self.send_subscription(&cmd_tx, subscription) {
            self.bar_types.remove(&key);
            let _ = cmd_tx.send(HandlerCommand::RemoveBarType { key });
            return Err(e);
        }
        Ok(())
    }

    /// Subscribe to funding rate updates for an instrument.
    pub async fn subscribe_funding_rates(&self, instrument_id: InstrumentId) -> anyhow::Result<()> {
        self.subscribe_asset_context_data(instrument_id, AssetContextDataType::FundingRate)
            .await
    }

    /// Subscribe to open interest updates for an instrument.
    pub async fn subscribe_open_interest(&self, instrument_id: InstrumentId) -> anyhow::Result<()> {
        self.subscribe_asset_context_data(instrument_id, AssetContextDataType::OpenInterest)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the handler task is alive before subscribing; respawn/reconnect the client if not.
  2. Do not call subscribe_bars concurrently with disconnect/shutdown.
  3. If the handler loop exited, create a fresh client; the channel cannot be reopened.
  4. Check handler-task logs for the root cause of the exit.

Example fix

// before
client.disconnect().await?;
client.subscribe_bars(bar_type).await?; // channel closed

// after
client.subscribe_bars(bar_type).await?;
client.disconnect().await?;
Defensive patterns

Strategy: try-catch

Validate before calling

// rust
if !client.is_connected() {
    return Err(anyhow::anyhow!("cannot subscribe bars: handler channel closed"));
}

Try / catch

// rust
if let Err(e) = client.subscribe_bars(bar_type).await {
    if e.to_string().contains("AddBarType") {
        tracing::warn!("bar subscribe lost handler ({e}); reconnecting");
        client = reconnect_client().await?;
        client.subscribe_bars(bar_type).await?;
    } else { return Err(e); }
}

Prevention

When it happens

Trigger: Calling subscribe_bars when the handler task has exited between the UpdateInstrument send and the AddBarType send, or already before — after disconnect(), handler panic, or task cancellation.

Common situations: Shutdown racing a bar subscribe; handler task aborted via its JoinHandle; stale client instance reused post-teardown; handler panic on prior traffic.

Related errors


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