nautechsystems/nautilus_trader · critical · anyhow::Error
Execution transaction {transaction_hash} conflicts with its
Error message
Execution transaction {transaction_hash} conflicts with its persisted record What it means
The execution_transaction insert uses ON CONFLICT (chain_id, transaction_hash) DO UPDATE ... WHERE every existing column (wallet_address, nonce, purpose, status, client_order_id) equals the incoming values, making exact re-insertion idempotent. rows_affected() == 0 (enforced at crates/adapters/blockchain/src/cache/database.rs:3027) means a row for that (chain_id, transaction_hash) already exists with different metadata - hash reuse or record mismatch - which the signer refuses rather than overwrite.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:3027
WHERE execution_transaction.wallet_address = EXCLUDED.wallet_address
AND execution_transaction.nonce = EXCLUDED.nonce
AND execution_transaction.purpose = EXCLUDED.purpose
AND execution_transaction.status = EXCLUDED.status
AND execution_transaction.client_order_id IS NOT DISTINCT FROM EXCLUDED.client_order_id
",
)
.bind(chain_id as i32)
.bind(wallet_address)
.bind(nonce as i64)
.bind(transaction_hash)
.bind(purpose)
.bind(status)
.bind(client_order_id)
.execute(&self.pool)
.await
.map_err(|e| anyhow::anyhow!("Failed to insert into execution_transaction table: {e}"))?;
anyhow::ensure!(
result.rows_affected() == 1,
"Execution transaction {transaction_hash} conflicts with its persisted record"
);
Ok(())
}
/// Installs execution schema version 2 without changing existing transaction rows.
///
/// The migration locks the legacy transaction table, refuses unresolved version 1 rows,
/// installs the versioned intent and hash-history tables, and fences older writers before
/// releasing the lock. This prevents a mixed-version process from bypassing the new signer
/// ownership constraints.
///
/// # Errors
///
/// Returns an error if the database operation fails.
pub async fn ensure_execution_transaction_schema(&self) -> anyhow::Result<()> {
let mut transaction = selfView on GitHub (pinned to 2114cf6f76)
Solutions
- Query the persisted row: SELECT * FROM execution_transaction WHERE chain_id = $1 AND transaction_hash = $2; and diff each column against the incoming values to find the mismatched field.
- If the status legitimately advanced, apply the status change through the update path rather than re-inserting new metadata.
- Ensure one writer path per wallet and never reuse a transaction hash across orders or purposes.
- If the row is provably wrong, repair it deliberately with an audited migration/fixup instead of forcing the insert.
Defensive patterns
Strategy: validation
Validate before calling
-- Before recording, check the persisted row for this hash SELECT wallet_address, nonce, purpose, status, client_order_id FROM execution_transaction WHERE chain_id = $1 AND transaction_hash = $2; -- If a row exists, every column must match the values you are about to record
Try / catch
if let Err(e) = db.add_execution_transaction(...).await {
if e.to_string().contains("conflicts with its persisted record") {
// data integrity violation: halt, investigate the divergent row, never force-overwrite
log::error!("tx hash reuse or metadata mismatch detected: {e}");
return Err(e);
}
return Err(e);
} Prevention
- Never reuse a transaction hash across orders, purposes, or wallets.
- Apply status changes through updates, not by re-inserting new metadata for the same hash.
- Keep one writer path per wallet and audit any manual edits to execution_transaction.
- Alert on this error: it indicates record divergence that could hide double execution.
When it happens
Trigger: Re-recording an existing transaction hash with any changed field: a different client_order_id or purpose for the same hash, a status transition submitted as a re-insert instead of an update, stale queued records replayed after a wallet/nonce change, or a genuine hash collision across wallets.
Common situations: An upstream bug assigning one tx hash to two operations; restoring from backup and replaying recorded transactions with updated statuses; multiple signer processes sharing the database with mismatched wallet configurations; manual database edits.
Related errors
- Execution intent {intent_id} has no current hash
- Execution intent {intent_id} has more than one current hash
- Failed to insert into execution_transaction table: {e}
- Execution transaction hash {transaction_hash} was not found
- Replacement hash {transaction_hash} conflicts with another i
AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21).
Data as JSON: /api/errors/a81106ec9a7a8ef5.
Report an issue: GitHub.