nautechsystems/nautilus_trader · error

Failed to send query add_signal to database message handler:

Error message

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

What it means

add_signal sends DatabaseQuery::AddSignal through the cache's mpsc channel to the database message handler and converts a send failure into this anyhow error. A failed send means the handler's receiver was dropped — the background task that persists signals is no longer running. The signal value is unaffected; delivery is impossible.

Source

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

                    }
                }
                Err(e) => {
                    log::error!("Failed to load bars for instrument {instrument_id}: {e:?}");
                    if let Err(e) = tx.send(Vec::new()) {
                        log::error!(
                            "Failed to send empty bars for instrument {instrument_id}: {e:?}"
                        );
                    }
                }
            }
        });
        Ok(rx.recv()?)
    }

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

    fn load_signals(&self, name: &str) -> anyhow::Result<Vec<Signal>> {
        let pool = self.pool.clone();
        let name = name.to_owned();
        let (tx, rx) = std::sync::mpsc::channel();

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

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the adapter/handler task is running before publishing signals
  2. Rebuild the cache adapter if it was shut down instead of reusing it
  3. Diagnose the handler's exit cause (panic, DB failure) from logs
  4. Catch the error if signal persistence is optional for the run
Defensive patterns

Strategy: try-catch

Try / catch

try:
    cache.add_signal(signal)
except Exception as e:
    if "Failed to send query add_signal" in str(e):
        logger.error("signal persistence failed: handler task gone")
        raise

Prevention

When it happens

Trigger: Calling add_signal (or py_add_signal) once the handler task has exited: adapter stopped, task aborted, or handler failed during initialization.

Common situations: Signal publication near the end of a run after the database adapter was shut down; a handler crash from an earlier query leaving the channel closed; scripts reusing a cache built in a finished async context.

Related errors


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