nautechsystems/nautilus_trader · error

Failed to insert into trade table: {e}

Error message

Failed to insert into trade table: {e}

What it means

This error wraps any sqlx failure from the INSERT INTO `trade` statement in `add_trade`. The library converts the raw database error into a message naming the operation. It means the TradeTick row was not persisted.

Source

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

                $1, $2, $3, $4::aggressor_side, $5, $6, $7, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP
            )
            ON CONFLICT (id)
            DO UPDATE
            SET
                instrument_id = $1, price = $2, quantity = $3, aggressor_side = $4, venue_trade_id = $5,
                ts_event = $6, ts_init = $7, updated_at = CURRENT_TIMESTAMP
        "#)
            .bind(trade.instrument_id.to_string())
            .bind(trade.price.to_string())
            .bind(trade.size.to_string())
            .bind(AggressorSidePg(trade.aggressor_side))
            .bind(trade.trade_id.to_string())
            .bind(trade.ts_event.to_string())
            .bind(trade.ts_init.to_string())
            .execute(pool)
            .await
            .map(|_| ())
            .map_err(|e| anyhow::anyhow!("Failed to insert into trade table: {e}"))
    }

    /// Loads all `TradeTick` entries for `instrument_id` via the provided `pool`.
    ///
    /// # Errors
    ///
    /// Returns an error if the SQL SELECT or deserialization fails.
    pub async fn load_trades(
        pool: &PgPool,
        instrument_id: &InstrumentId,
    ) -> anyhow::Result<Vec<TradeTick>> {
        sqlx::query_as::<_, TradeTickRow>(
            r#"SELECT * FROM "trade" WHERE instrument_id = $1 ORDER BY ts_event ASC"#,
        )
        .bind(instrument_id.to_string())
        .fetch_all(pool)
        .await
        .map(|rows| rows.into_iter().map(|row| row.0).collect())

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Run migrations so the `trade` table and its enum types exist
  2. Check the wrapped sqlx error for constraint/type issues (column mismatch, invalid enum label) and align the schema or data
  3. Verify pool connectivity before batch inserts
  4. Use a transaction so failed trade inserts can be retried as a batch
Defensive patterns

Strategy: try-catch

Validate before calling

sqlx::query("SELECT 1 FROM trade LIMIT 1").fetch_optional(pool).await.map_err(|e| anyhow::anyhow!("trade table check: {e}"))?;

Try / catch

if let Err(e) = add_trade(&pool, &trade).await {
    tracing::error!("trade insert failed: {e:#}");
    return Err(e.into());
}

Prevention

When it happens

Trigger: Calling `add_trade(pool, &trade)` when the `trade` table does not exist, a column type/constraint fails (e.g. instrument_id or enum casting), the connection drops mid-insert, or the pool is closed.

Common situations: Migrations not run in a new environment; schema drift after upgrading (missing columns); enum values not representable in the Postgres enum types; DB credentials or network failures.

Related errors


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