nautechsystems/nautilus_trader · error

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

Error message

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

What it means

Thrown by the SQL cache's `add` when sending a `DatabaseQuery::Add(key, value)` over the internal mpsc channel to the dedicated database message-handler task fails. A send fails only when the receiver side has been dropped/shut down, meaning the writer task is no longer processing queries. The write is not performed.

Source

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

            "delete_order not implemented for PostgreSQL cache adapter: {client_order_id}"
        )
    }

    fn delete_position(&self, position_id: &PositionId) -> anyhow::Result<()> {
        anyhow::bail!("delete_position not implemented for PostgreSQL cache adapter: {position_id}")
    }

    fn delete_account_event(&self, account_id: &AccountId, event_id: &str) -> anyhow::Result<()> {
        anyhow::bail!(
            "delete_account_event not implemented for PostgreSQL cache adapter: {account_id}, {event_id}"
        )
    }

    fn add(&self, key: String, value: Bytes) -> anyhow::Result<()> {
        let query = DatabaseQuery::Add(key, value.into());
        self.tx
            .send(query)
            .map_err(|e| anyhow::anyhow!("Failed to send query to database message handler: {e}"))
    }

    fn add_currency(&self, currency: &Currency) -> anyhow::Result<()> {
        let query = DatabaseQuery::AddCurrency(*currency);
        self.tx.send(query).map_err(|e| {
            anyhow::anyhow!("Failed to query add_currency to database message handler: {e}")
        })
    }

    fn add_instrument(&self, instrument: &InstrumentAny) -> anyhow::Result<()> {
        let query = DatabaseQuery::AddInstrument(instrument.clone());
        self.tx.send(query).map_err(|e| {
            anyhow::anyhow!("Failed to send query add_instrument to database message handler: {e}")
        })
    }

    fn add_instrument_close(&self, close: &InstrumentClose) -> anyhow::Result<()> {
        self.tx

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the cache database adapter (and its message-handler task) is created, connected, and kept alive before calling `add`.
  2. Check logs from the receiver task for a panic that dropped the channel.
  3. Rebuild/reconnect the cache database client and retry the write.
  4. Guard Python code so `py_add` is not called after the adapter has been stopped.

Example fix

// before
self.tx.send(query).map_err(|e| anyhow::anyhow!("Failed to send query to database message handler: {e}"))?;
// after
if self.tx.is_closed() {
    return Err(anyhow::anyhow!("Database message handler is not running; cannot persist cache entry"));
}
self.tx.send(query).map_err(|e| anyhow::anyhow!("Failed to send query to database message handler: {e}"))?;
Defensive patterns

Strategy: try-catch

Validate before calling

# Python: verify adapter is writable before add
if not cache_db_adapter.is_running:
    raise RuntimeError("Cache database adapter is not running")

Try / catch

try:
    cache.add(key, value)
except RuntimeError as e:
    logger.error("Cache database writer unavailable: %s", e)

Prevention

When it happens

Trigger: Calling `add` (exposed to Python as `py_add`) after the backing task/thread running the message loop has exited or been dropped, so the `tx.send(query)` returns a SendError.

Common situations: Adapter shutdown ordering — cache database client stopped or its connection task panicked while the cache is still being written; a background panic killed the receiver; calling add during teardown/atexit from Python.

Related errors


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