nautechsystems/nautilus_trader · error

Failed to load general table: {e}

Error message

Failed to load general table: {e}

What it means

`DatabaseQueries::load` runs `SELECT * FROM general` and decodes every row into an `AHashMap<String, Vec<u8>>`, wrapping any sqlx failure in this anyhow error. Failure means the query or row decoding failed: connectivity loss, missing table, or schema/data drift where stored values no longer match the expected row types.

Source

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

    }

    /// Loads all entries from the `general` table via the provided `pool`.
    ///
    /// # Errors
    ///
    /// Returns an error if the SELECT operation fails.
    pub async fn load(pool: &PgPool) -> anyhow::Result<AHashMap<String, Vec<u8>>> {
        sqlx::query_as::<_, GeneralRow>("SELECT * FROM general")
            .fetch_all(pool)
            .await
            .map(|rows| {
                let mut cache: AHashMap<String, Vec<u8>> = AHashMap::new();
                for row in rows {
                    cache.insert(row.id, row.value);
                }
                cache
            })
            .map_err(|e| anyhow::anyhow!("Failed to load general table: {e}"))
    }

    /// Inserts or ignores a `Currency` row via the provided `pool`.
    ///
    /// # Errors
    ///
    /// Returns an error if the INSERT operation fails.
    pub async fn add_currency(pool: &PgPool, currency: Currency) -> anyhow::Result<()> {
        sqlx::query(
            "INSERT INTO currency (id, precision, iso4217, name, currency_type) VALUES ($1, $2, $3, $4, $5::currency_type) ON CONFLICT (id) DO NOTHING"
        )
            .bind(currency.code.as_str())
            .bind(i32::from(currency.precision))
            .bind(i32::from(currency.iso4217))
            .bind(currency.name.as_str())
            .bind(CurrencyTypePg(currency.currency_type))
            .execute(pool)
            .await

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Run the current migrations so the `general` table and column types match this crate version.
  2. Confirm DATABASE_URL targets the intended cache database.
  3. If rows were written by a mismatched version, clear or migrate the `general` table before loading.
  4. Read the inner sqlx error: 'relation does not exist' means run migrations; 'error decoding' means schema drift.
Defensive patterns

Strategy: try-catch

Validate before calling

let exists: bool = sqlx::query_scalar(
    "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'general')",
).fetch_one(pool).await?;
if !exists {
    return Err(anyhow::anyhow!("'general' table missing — run migrations"));
}

Try / catch

match DatabaseQueries::load(&pool).await {
    Err(e) if e.to_string().contains("relation \"general\" does not exist") => {
        return Err(anyhow::anyhow!("cache schema missing — run migrations: {e}"));
    }
    result => result?,
}

Prevention

When it happens

Trigger: Calling `DatabaseQueries::load(pool)` when the connection fails, the `general` table doesn't exist (migrations missing), or GeneralRow decoding fails because rows were written by a different schema version (e.g. column type changed between releases).

Common situations: Loading the cache on startup against a database created by an older or newer schema version; pointing at the wrong database; transient network failure during a large fetch.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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