nautechsystems/nautilus_trader · error · anyhow::Error

Failed to load signals: {e}

Error message

Failed to load signals: {e}

What it means

Raised by `load_signals` when the SELECT of all `signal` rows filtered by `name` fails at the database level. The raw sqlx error is wrapped with `anyhow::anyhow!` and this message; it does not indicate missing rows (that returns an empty vec), only that the query itself failed.

Source

Thrown at crates/infrastructure/src/sql/queries.rs:1608

        .await
        .map(|_| ())
        .map_err(|e| anyhow::anyhow!("Failed to insert into signal table: {e}"))
    }

    /// Loads all `Signal` entries by `name` via the provided `pool`.
    ///
    /// # Errors
    ///
    /// Returns an error if the SQL SELECT or deserialization fails.
    pub async fn load_signals(pool: &PgPool, name: &str) -> anyhow::Result<Vec<Signal>> {
        sqlx::query_as::<_, SignalRow>(
            r#"SELECT * FROM "signal" WHERE name = $1 ORDER BY ts_init ASC"#,
        )
        .bind(name)
        .fetch_all(pool)
        .await
        .map(|rows| rows.into_iter().map(|row| row.0).collect())
        .map_err(|e| anyhow::anyhow!("Failed to load signals: {e}"))
    }

    /// Inserts a `CustomData` entry via the provided `pool`.
    ///
    /// Serializes the model `CustomData` to full JSON and stores it in the JSONB `value` column.
    ///
    /// # Errors
    ///
    /// Returns an error if the SQL INSERT operation fails.
    pub async fn add_custom_data(pool: &PgPool, data: &CustomData) -> anyhow::Result<()> {
        let json_bytes = serde_json::to_vec(data)
            .map_err(|e| anyhow::anyhow!("CustomData must be valid JSON: {e}"))?;
        let value_json: serde_json::Value = serde_json::from_slice(&json_bytes)
            .map_err(|e| anyhow::anyhow!("CustomData value must be valid JSON: {e}"))?;
        let data_type_obj = value_json
            .get("data_type")
            .and_then(|v| v.as_object())
            .ok_or_else(|| anyhow::anyhow!("CustomData JSON missing data_type"))?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the wrapped sqlx error message for the DB-level cause.
  2. Ensure migrations created the `signal` table with a `name` column.
  3. Verify the pool points to the expected database.
  4. Add retry logic for transient connection failures.
Defensive patterns

Strategy: retry

Validate before calling

let exists: bool = sqlx::query_scalar(
    "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'signal')")
    .fetch_one(pool).await?;
anyhow::ensure!(exists, "signal table missing; run migrations");

Try / catch

let signals = retry_backoff(3, || load_signals(&pool, name.clone())).await
    .map_err(|e| e.context("loading signals failed after retries"))?;

Prevention

When it happens

Trigger: Calling `load_signals(pool, name)` when the `signal` table doesn't exist, the connection fails, pool is exhausted, or the query text/columns mismatch the current schema.

Common situations: Migrations not applied; using a pool pointing at the wrong database; transient network drops; PostgreSQL restart during the query.

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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