nautechsystems/nautilus_trader · error

Failed to send query add_quote to database message handler:

Error message

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

What it means

The PostgreSQL cache adapter writes are asynchronous: add_quote wraps the QuoteTick in a DatabaseQuery::AddQuote and sends it over an mpsc channel (self.tx) to a background database message handler task. This error is raised when that send fails, which only happens if the receiver half held by the handler task has been dropped — i.e. the background writer task has shut down or was never started. The tick itself is fine; the failure is in the cache's internal messaging plumbing.

Source

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

    }

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

    fn add_order_book(&self, _order_book: &OrderBook) -> anyhow::Result<()> {
        todo!()
    }

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

    fn load_quotes(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<QuoteTick>> {
        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_quotes(&pool, &instrument_id).await;
            match result {
                Ok(quotes) => {
                    if let Err(e) = tx.send(quotes) {
                        log::error!("Failed to send quotes for instrument {instrument_id}: {e:?}");
                    }
                }
                Err(e) => {
                    log::error!("Failed to load quotes for instrument {instrument_id}: {e:?}");

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the PostgreSQL cache adapter is fully started and its background handler task is alive before writing quotes
  2. Recreate or restart the cache/database adapter if it was shut down, instead of reusing the old instance
  3. Check handler task logs for a panic or early exit (e.g. failed pool connection) and fix the underlying startup cause
  4. If writes are optional, catch the anyhow error from add_quote and degrade gracefully rather than crashing the strategy

Example fix

// before: writing through a stale adapter after shutdown
cache.add_quote(&quote)?;
// after: rebuild the adapter when the handler channel is closed
if let Err(e) = cache.add_quote(&quote) {
    if e.to_string().contains("Failed to send query add_quote") {
        cache = CacheDatabaseAdapter::new(config)?; // restart handler task
        cache.add_quote(&quote)?;
    } else { return Err(e); }
}
Defensive patterns

Strategy: try-catch

Try / catch

// Python
try:
    cache.add_quote(quote)
except Exception as e:
    if "Failed to send query add_quote" in str(e):
        logger.error("DB cache handler down; reinitializing adapter")
        cache = rebuild_cache_adapter()
    else:
        raise
// Rust
if let Err(e) = cache.add_quote(&quote) {
    tracing::error!("quote persistence failed: {e:#}");
}

Prevention

When it happens

Trigger: Calling add_quote (directly from Rust or via py_add_quote from Python) after the database message handler task has exited, e.g. the connection pool/task was aborted, the cache was shut down, or the handler task panicked on startup.

Common situations: Closing or dropping the cache/database adapter while a live strategy or script still writes quotes; a startup failure of the message handler (bad DB config) leaving the receiver dropped; holding a stale cache instance after disconnect during a long-running backtest/live session.

Related errors


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