nautechsystems/nautilus_trader · error

Failed to insert into account table: {e}

Error message

Failed to insert into account table: {e}

What it means

add_account upserts a row into the "account" table (INSERT ... ON CONFLICT DO NOTHING) before writing the account_event row. Failure of this INSERT is wrapped with this message. As with the trader insert, the cause is DB-level: connectivity, missing schema, or privileges.

Source

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

        let balances = event
            .get("balances")
            .cloned()
            .ok_or_else(|| anyhow::anyhow!("Serialized account event has no balances"))?;
        let margins = event
            .get("margins")
            .cloned()
            .ok_or_else(|| anyhow::anyhow!("Serialized account event has no margins"))?;

        sqlx::query(
            r#"
            INSERT INTO "account" (id) VALUES ($1) ON CONFLICT (id) DO NOTHING
        "#,
        )
        .bind(account_event.account_id.to_string())
        .execute(&mut *transaction)
        .await
        .map(|_| ())
        .map_err(|e| anyhow::anyhow!("Failed to insert into account table: {e}"))?;

        sqlx::query(r#"
            INSERT INTO "account_event" (
                id, kind, account_id, base_currency, balances, margins, is_reported, ts_event, ts_init, created_at, updated_at
            ) VALUES (
                $1, $2, $3, $4, $5, $6, $7, $8, $9, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
            )
            ON CONFLICT (id)
            DO UPDATE
            SET
                kind = $2, account_id = $3, base_currency = $4, balances = $5, margins = $6, is_reported = $7,
                ts_event = $8, ts_init = $9, updated_at = CURRENT_TIMESTAMP
        "#)
            .bind(account_event.event_id.to_string())
            .bind(account_event.account_type.to_string())
            .bind(account_event.account_id.to_string())
            .bind(account_event.base_currency.map(|x| x.code.as_str()))
            .bind(balances)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Check the embedded sqlx error: 'relation "account" does not exist' means apply migrations.
  2. Verify connection string, network reachability, and pool health.
  3. Grant INSERT on the account table to the application's DB role.
  4. Retry transient connection errors with backoff; the open transaction is rolled back automatically on drop.

Example fix

// before: failing silently deep inside add_account
psql -c "\dt"  # account table missing

// after: apply schema before use
sqlx migrate run  # or apply the crate's SQL schema, then retry add_account
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight table existence and privilege
sqlx::query("SELECT to_regclass('\"account\"') IS NOT NULL AND has_table_privilege(current_user, '\"account\"', 'INSERT')")
    .fetch_one(pool).await?;

Try / catch

match add_account(&pool, updated, state).await {
    Err(e) if is_transient(&e) => retry_with_backoff(|| add_account(&pool, updated, state.clone())).await?,
    Err(e) => return Err(e),
    Ok(()) => {},
}

Prevention

When it happens

Trigger: Calling add_account when the INSERT INTO "account" fails — no "account" table (migrations not applied), connection failure, or insufficient INSERT privilege.

Common situations: Fresh database without migrations run; Postgres restarted or idle connection reaped mid-call; DB role granted rights only on event tables; connecting to the wrong database/tenant schema.

Related errors


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