nautechsystems/nautilus_trader · error
Failed to validate order client origin: {e}
Error message
Failed to validate order client origin: {e} What it means
index_order_clients first checks whether a client_order_id is already claimed by a different execution client; any sqlx failure during that fetch_optional lookup is wrapped as 'Failed to validate order client origin'. It is a database-layer failure of the conflict-check SELECT, not the conflict itself (that is the separate bail below). Thrown so the whole transaction aborts with a clear message.
Source
Thrown at crates/infrastructure/src/sql/queries.rs:1461
let mut transaction = pool.begin().await?;
for (client_order_id, client_id) in claims {
let conflicting_client_id = sqlx::query_scalar::<_, String>(
r#"
SELECT client_id
FROM "order_event"
WHERE client_order_id = $1
AND client_id IS NOT NULL
AND client_id <> $2
LIMIT 1
"#,
)
.bind(client_order_id.to_string())
.bind(client_id.to_string())
.fetch_optional(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to validate order client origin: {e}"))?;
if let Some(conflicting_client_id) = conflicting_client_id {
anyhow::bail!(
"Order {client_order_id} is already claimed by execution client \
{conflicting_client_id} and cannot be claimed by {client_id}"
);
}
sqlx::query(
r#"
INSERT INTO "client" (id)
VALUES ($1)
ON CONFLICT (id) DO NOTHING
"#,
)
.bind(client_id.to_string())
.execute(&mut *transaction)
.awaitView on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the error's source chain for the raw sqlx/DB message.
- Initialize the cache schema (order_event, client tables) before calling index_order_clients.
- Retry on transient connection/lock errors; reduce concurrency or shorten the transaction.
- Verify the transaction was opened on a healthy connection.
Example fix
// before
queries::postgres::index_order_clients(&mut tx, client_order_id, client_id).await?;
// after
match queries::postgres::index_order_clients(&mut tx, client_order_id, client_id).await {
Ok(()) => (),
Err(e) if is_transient(&e) => retry_indexing(client_order_id, client_id).await?,
Err(e) => return Err(e),
} Defensive patterns
Strategy: retry
Validate before calling
let db_ready = sqlx::query(r#"SELECT 1 FROM \"order_event\" LIMIT 1"#).fetch_optional(pool).await.is_ok(); anyhow::ensure!(db_ready, "order_event schema missing before index_order_clients");
Try / catch
for attempt in 0..3 {
match index_clients_once(&pool, &client_order_id, &client_id).await {
Ok(_) => break,
Err(e) if is_transient(&e) && attempt < 2 => tokio::time::sleep(backoff(attempt)).await,
Err(e) => return Err(e),
}
} Prevention
- Initialize schema before rebuild_index/connect flows
- Serialize index rebuilds; avoid concurrent writers on order_event
- Keep transactions short to reduce lock contention
- Configure pool timeouts/keepalives for long rebuilds
When it happens
Trigger: Calling index_order_clients when the SELECT used for the conflicting-client check fails: `order_event`/related table missing, malformed query/schema mismatch, transaction connection lost, or lock timeouts.
Common situations: Cache DB not initialized before rebuild_index/connect; concurrent writers holding locks on order_event rows; DB connection dropped mid-transaction.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- Failed to insert into trader table: {e}
- Failed to insert into order table: {e}
- Failed to insert into position table: {e}
- Failed to delete position_event rows: {e}
- Failed to insert into bar table: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a172466b872bf1cd.
Report an issue: GitHub.