nautechsystems/nautilus_trader · error

Failed to send query add_bar to database message handler: {e

Error message

Failed to send query add_bar to database message handler: {e}

What it means

add_bar wraps the Bar in DatabaseQuery::AddBar and sends it over self.tx to the database message handler; on send failure anyhow::anyhow! raises this error. Sends fail only when the receiver half was dropped, i.e. the background writer task is gone. Note the message text says add_bar while some sibling methods (e.g. add_custom_data) reuse other labels — the mechanism is identical.

Source

Thrown at crates/infrastructure/src/sql/cache.rs:1065

        });
        Ok(rx.recv()?)
    }

    fn add_funding_rate(&self, _funding_rate: &FundingRateUpdate) -> anyhow::Result<()> {
        anyhow::bail!("add_funding_rate not implemented for PostgreSQL cache adapter")
    }

    fn load_funding_rates(
        &self,
        _instrument_id: &InstrumentId,
    ) -> anyhow::Result<Vec<FundingRateUpdate>> {
        anyhow::bail!("load_funding_rates not implemented for PostgreSQL cache adapter")
    }

    fn add_bar(&self, bar: &Bar) -> anyhow::Result<()> {
        let query = DatabaseQuery::AddBar(bar.to_owned());
        self.tx.send(query).map_err(|e| {
            anyhow::anyhow!("Failed to send query add_bar to database message handler: {e}")
        })
    }

    fn load_bars(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<Bar>> {
        let pool = self.pool.clone();
        let instrument_id = instrument_id.to_owned();
        let (tx, rx) = std::sync::mpsc::channel();

        tokio::spawn(async move {
            let result = DatabaseQueries::load_bars(&pool, &instrument_id).await;
            match result {
                Ok(bars) => {
                    if let Err(e) = tx.send(bars) {
                        log::error!("Failed to send bars for instrument {instrument_id}: {e:?}");
                    }
                }
                Err(e) => {
                    log::error!("Failed to load bars for instrument {instrument_id}: {e:?}");

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the cache adapter and its handler task are alive before bar writes
  2. Recreate the adapter after shutdown rather than reusing the dropped-channel instance
  3. Find and fix why the handler task ended (startup failure, panic) from its logs
  4. Guard bar writes with error handling when persistence loss is acceptable
Defensive patterns

Strategy: try-catch

Try / catch

try:
    cache.add_bar(bar)
except Exception as e:
    if "Failed to send query add_bar" in str(e):
        logger.warning("bar persistence skipped: handler down")
        return  # or buffer bar for retry

Prevention

When it happens

Trigger: Calling add_bar (or py_add_bar) after the handler task exited — adapter shutdown, aborted task, or handler that died at startup.

Common situations: Bar persistence during a long backfill while the DB adapter was recreated or disconnected; shutdown ordering bug where the cache is dropped before writers stop; handler crashed earlier on a bad batch.

Related errors


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