nautechsystems/nautilus_trader · error

Failed to insert into client table: {e}

Error message

Failed to insert into client table: {e}

What it means

Wraps failure of the INSERT into the client table performed by `add_order_event` when a `client_id` is supplied (a temporary step until client initialization is implemented). This runs inside the same transaction, so any failure rolls back the trader insert as well. Typically caused by constraint violations or connectivity issues on the client table.

Source

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

        .bind(order_event.trader_id().to_string())
        .execute(&mut *transaction)
        .await
        .map(|_| ())
        .map_err(|e| anyhow::anyhow!("Failed to insert into trader table: {e}"))?;

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

        let exec_algorithm_params = order_event
            .exec_algorithm_params()
            .map(serde_json::to_value)
            .transpose()
            .map_err(|e| anyhow::anyhow!("Failed to serialize exec algorithm params: {e}"))?;
        let info = order_event
            .info()
            .map(serde_json::to_value)
            .transpose()
            .map_err(|e| anyhow::anyhow!("Failed to serialize order event info: {e}"))?;

        sqlx::query(r#"
            INSERT INTO "order_event" (
                id, kind, client_order_id, order_type, order_side, trader_id, client_id, reason, strategy_id, instrument_id, trade_id, currency, quantity, time_in_force, liquidity_side,
                post_only, reduce_only, quote_quantity, reconciliation, price, last_px, last_qty, trigger_price, trigger_type, limit_offset, trailing_offset,
                trailing_offset_type, expire_time, display_qty, emulation_trigger, trigger_instrument_id, contingency_type,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect `{e}` for the exact sqlx cause (unique violation vs connection).
  2. Run migrations so the client table exists with the expected schema.
  3. Ensure the client_id string form matches what the schema expects.
  4. Retry `add_order_event` — the transaction rolls back cleanly on failure.

Example fix

// before
add_order_event(&mut tx, &event, Some(client_id.clone())).await?;
// after
add_order_event(&mut tx, &event, Some(client_id.clone())).await
    .with_context(|| format!("persisting event for client {client_id}"))?;
Defensive patterns

Strategy: try-catch

Validate before calling

sqlx::query(r#"SELECT EXISTS(SELECT 1 FROM "client" WHERE id = $1)"#)
    .bind(client_id.to_string())
    .fetch_one(pool).await
    .map_err(|e| anyhow::anyhow!("client table unavailable: {e}"))?;

Type guard

fn is_duplicate_client(e: &anyhow::Error) -> bool {
    e.to_string().contains("duplicate key")
}

Try / catch

match add_order_event(&mut tx, &event, Some(client_id.clone())).await {
    Ok(()) => {}
    Err(e) if is_duplicate_client(&e) => { /* client already registered — safe to continue */ }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `add_order_event` with `Some(client_id)` when the client table is missing, the client row violates a unique/constraint rule, or the connection fails mid-transaction.

Common situations: Schema not migrated; duplicate client_id with conflicting columns; database failover during write; client_id string format mismatched with the column type.

Related errors


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