nautechsystems/nautilus_trader · error

Failed to insert into trader table: {e}

Error message

Failed to insert into trader table: {e}

What it means

Raised by `add_order_snapshot` in the first step of its two-step transaction: inserting the trader row into the `trader` table fails. The error occurs before the `order` insert, so the whole transaction aborts and nothing is committed. The sqlx error is wrapped in `anyhow` with this message.

Source

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

    #[expect(
        clippy::too_many_lines,
        reason = "order snapshot persistence maps the full database schema in one transaction"
    )]
    pub async fn add_order_snapshot(pool: &PgPool, snapshot: OrderSnapshot) -> anyhow::Result<()> {
        let mut transaction = pool.begin().await?;

        // Insert trader if it does not exist
        // TODO remove this when node and trader initialization is implemented
        sqlx::query(
            r#"
            INSERT INTO "trader" (id) VALUES ($1) ON CONFLICT (id) DO NOTHING
            "#,
        )
        .bind(snapshot.trader_id.to_string())
        .execute(&mut *transaction)
        .await
        .map(|_| ())
        .map_err(|e| anyhow::anyhow!("Failed to insert into trader table: {e}"))?;

        sqlx::query(
            r#"
            INSERT INTO "order" (
                id, trader_id, strategy_id, instrument_id, client_order_id, venue_order_id, position_id,
                account_id, last_trade_id, order_type, order_side, quantity, price, trigger_price,
                trigger_type, limit_offset, trailing_offset, trailing_offset_type, time_in_force,
                expire_time, filled_qty, liquidity_side, avg_px, slippage, commissions, status,
                is_post_only, is_reduce_only, is_quote_quantity, display_qty, emulation_trigger,
                trigger_instrument_id, contingency_type, order_list_id, linked_order_ids,
                parent_order_id, exec_algorithm_id, exec_algorithm_params, exec_spawn_id, tags, init_id, ts_init, ts_last,
                activation_price, created_at, updated_at
            ) VALUES (
                $1, $2, $3, $4, $1, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16,
                $17::TRAILING_OFFSET_TYPE, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28,
                $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39, $40, $41, $42, $43,
                CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
            )

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the wrapped `{e}` for the underlying constraint or connection error.
  2. Apply schema migrations so the `trader` table exists with expected columns.
  3. Use upsert / ON CONFLICT handling if snapshots for the same trader_id can be written repeatedly.
  4. Check connection stability; if transient, retry the whole `add_order_snapshot` call (the transaction rolled back atomically).

Example fix

// before
add_order_snapshot(&pool, &snapshot).await?;

// after: idempotent retry on failure
for attempt in 0..3 {
    match add_order_snapshot(&pool, &snapshot).await {
        Ok(()) => break,
        Err(e) if attempt < 2 && is_connection_error(&e) => continue,
        Err(e) => return Err(e),
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

let ok = sqlx::query_scalar::<_, i64>(
    "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'trader'")
    .fetch_one(pool).await? > 0;
if !ok { return Err(anyhow::anyhow!("trader table missing: run migrations")); }

Try / catch

if let Err(e) = add_order_snapshot(&pool, &snapshot).await {
    tracing::error!("order snapshot not persisted (rolled back): {e:#}");
    // Transaction is atomic; safe to retry once connectivity is confirmed.
    return Err(e);
}

Prevention

When it happens

Trigger: Calling `add_order_snapshot(pool, snapshot)` when the `trader` table is missing; the trader_id violates a constraint; a bound value's format is rejected by a column type; or the connection acquired for the transaction drops before the insert completes.

Common situations: Fresh database without migrations; re-inserting a trader row that already exists without conflict handling; schema drift between crate versions; network interruption between the app and PostgreSQL during snapshot writes.

Related errors


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