nautechsystems/nautilus_trader · error

Failed to insert into currency table: {e}

Error message

Failed to insert into currency table: {e}

What it means

`DatabaseQueries::add_currency` inserts a `Currency` row with `ON CONFLICT (id) DO NOTHING` (duplicate ids are silently ignored, never errors) and wraps any other sqlx failure in this anyhow error. Common causes are a missing `currency` table, connection failure, or — most often — an invalid value for the Postgres `currency_type` enum cast because the database enum is outdated relative to the binary.

Source

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

    /// 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
            .map(|_| ())
            .map_err(|e| anyhow::anyhow!("Failed to insert into currency table: {e}"))
    }

    /// Loads all `Currency` entries via the provided `pool`.
    ///
    /// # Errors
    ///
    /// Returns an error if the SELECT operation fails.
    pub async fn load_currencies(pool: &PgPool) -> anyhow::Result<Vec<Currency>> {
        sqlx::query_as::<_, CurrencyRow>("SELECT * FROM currency ORDER BY id ASC")
            .fetch_all(pool)
            .await
            .map(|rows| rows.into_iter().map(|row| row.0).collect())
            .map_err(|e| anyhow::anyhow!("Failed to load currencies: {e}"))
    }

    /// Loads a single `Currency` entry by `code` via the provided `pool`.
    ///
    /// # Errors

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Run migrations so the `currency` table and `currency_type` enum include all variants used by this code version.
  2. If the inner error says 'invalid input value for enum currency_type', update the DB enum (ALTER TYPE ... ADD VALUE) or upgrade the code.
  3. Verify connectivity and INSERT privileges on `currency`.
  4. Remember duplicates are ignored by design; a raised error indicates a real failure, not a duplicate id.

Example fix

-- before: stale DB enum
-- after: add the missing variant
ALTER TYPE currency_type ADD VALUE IF NOT EXISTS 'crypto';
Defensive patterns

Strategy: try-catch

Validate before calling

let known: Vec<String> = sqlx::query_scalar(
    "SELECT enumlabel FROM pg_enum e JOIN pg_type t ON e.enumtypid = t.oid WHERE t.typname = 'currency_type'",
).fetch_all(pool).await?;
// ensure every CurrencyType variant this binary can emit is representable in the DB enum

Try / catch

if let Err(e) = DatabaseQueries::add_currency(&pool, currency).await {
    if e.to_string().contains("invalid input value for enum") {
        return Err(anyhow::anyhow!(
            "DB currency_type enum outdated vs binary — run migrations/upgrade: {e}"
        ));
    }
    return Err(e);
}

Prevention

When it happens

Trigger: Calling `DatabaseQueries::add_currency(pool, currency)` when: migrations weren't applied (table or `currency_type` enum type missing); `currency.currency_type` can't be cast to the DB enum (newer CurrencyType variant not in the DB); precision/iso4217 violate column constraints; connection drops.

Common situations: Version skew where code emits a new currency_type the database enum lacks; fresh database without migrations; restricted DB role without INSERT privilege.

Related errors


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