nautechsystems/nautilus_trader · error

Failed to send query add_order_snapshot to database message

Error message

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

What it means

Thrown by `add_position_snapshot` when the `DatabaseQuery::AddPositionSnapshot` message cannot be sent to the database message-handler task because the receiving end of the channel was dropped. The snapshot is not persisted. Despite the message text, this is the position-snapshot write path.

Source

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

        self.tx.send(query).map_err(|e| {
            anyhow::anyhow!(
                "Failed to send query add_order_snapshot to database message handler: {e}"
            )
        })
    }

    fn add_position(&self, position: &Position) -> anyhow::Result<()> {
        let event = position_last_event(position)?;
        let query = DatabaseQuery::AddPosition(position.id, event);
        self.tx.send(query).map_err(|e| {
            anyhow::anyhow!("Failed to send query add_position to database message handler: {e}")
        })
    }

    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();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the adapter is running before taking position snapshots.
  2. Check for panics in the message-handler task.
  3. Reconnect/recreate the adapter and re-issue the snapshot.
  4. Sequence shutdown so snapshots are flushed before the writer stops.

Example fix

// before
self.tx.send(query).map_err(|e| anyhow::anyhow!("Failed to send query add_position_snapshot to database message handler: {e}"))?;
// after
if self.tx.is_closed() {
    return Err(anyhow::anyhow!("Cannot persist position snapshot: database message handler is stopped"));
}
self.tx.send(query).map_err(|e| anyhow::anyhow!("Failed to send query add_position_snapshot to database message handler: {e}"))?;
Defensive patterns

Strategy: try-catch

Validate before calling

if not cache_db_adapter.is_running:
    raise RuntimeError("Cannot snapshot position state: adapter not running")

Try / catch

try:
    cache.add_position_snapshot(snapshot)
except RuntimeError as e:
    logger.error("Position snapshot persistence failed: %s", e)

Prevention

When it happens

Trigger: Calling `add_position_snapshot` (via `py_add_position_snapshot` or `snapshot_position_state`) when the writer task has exited or panicked.

Common situations: Snapshotting position state during shutdown; dead writer task from an earlier handler crash.

Related errors


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