nautechsystems/nautilus_trader · error · anyhow::Error
Failed to retire replaced execution hash: {e}
Error message
Failed to retire replaced execution hash: {e} What it means
Wrapped sqlx error from the UPDATE execution_transaction_hash SET current = FALSE, status = 'replaced' WHERE intent_id = $1 AND current statement inside add_execution_replacement_hash (database.rs:3804-3816). This retires the intent's current hash row before inserting the replacement hash. Note it is not an error if zero rows match (a fresh intent may have no current row); this error means the UPDATE itself failed.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:3816
.await
.map_err(|e| anyhow::anyhow!("Failed to lock active execution intent {intent_id}: {e}"))?
.ok_or_else(|| anyhow::anyhow!("Active execution intent {intent_id} was not found"))?;
anyhow::ensure!(
execution_transition_allowed(¤t_status, TransactionStatus::Replaced),
"Invalid execution transition for intent {intent_id}: {current_status} -> replaced"
);
sqlx::query(
"
UPDATE execution_transaction_hash
SET current = FALSE, status = 'replaced', updated_at = NOW()
WHERE intent_id = $1 AND current
",
)
.bind(intent_id)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to retire replaced execution hash: {e}"))?;
let row = sqlx::query_as::<_, ExecutionTransactionHashRow>(
"
INSERT INTO execution_transaction_hash (
intent_id, chain_id, transaction_hash, status, current
) VALUES ($1, $2, $3, 'replaced', TRUE)
ON CONFLICT (chain_id, transaction_hash) DO UPDATE
SET current = TRUE, updated_at = NOW()
WHERE execution_transaction_hash.intent_id = EXCLUDED.intent_id
RETURNING
id, intent_id, chain_id, transaction_hash, raw_transaction, status,
block_number, block_hash, receipt_success, gas_used,
effective_gas_price, current
",
)
.bind(intent_id)
.bind(chain_id_db)
.bind(transaction_hash)View on GitHub (pinned to 2114cf6f76)
Solutions
- Downcast to sqlx::Error and read the database error to identify constraint vs connectivity causes
- Apply migrations so execution_transaction_hash has current, status (with 'replaced' allowed), and updated_at
- Retry the whole add_execution_replacement_hash call for transient classes - the transaction rolled back, so nothing was retired
- If a status CHECK rejected 'replaced', widen the constraint in a migration
Defensive patterns
Strategy: retry
Type guard
fn is_transient_db_error(err: &anyhow::Error) -> bool {
err.downcast_ref::<sqlx::Error>().map_or(false, |e| {
matches!(e, sqlx::Error::PoolTimedOut | sqlx::Error::PoolClosed | sqlx::Error::Io(_))
|| e.as_database_error().and_then(|d| d.code()).map_or(false, |c| {
matches!(c.as_ref(), "40001" | "40P01" | "55P03" | "57014" | "08000" | "08003" | "08006")
})
})
} Try / catch
match db.add_execution_replacement_hash(intent_id, chain_id, &hash).await {
Ok(row) => Ok(row),
Err(e) if is_transient_db_error(&e) => retry_with_backoff(e),
Err(e) => Err(e), // constraint causes: fix schema/status value, do not retry
} Prevention
- Include 'replaced' in any status CHECK constraint when migrating replacement support
- Keep the current flag column indexed (intent_id, current) for cheap retirement
- Run migrations atomically with code deploys so retire/upsert statements always match the schema
When it happens
Trigger: Connection loss or timeout during the retire UPDATE; a CHECK constraint rejecting the 'replaced' status value; schema drift on the current/status/updated_at columns; the server aborting the transaction between the FOR UPDATE lock and this statement.
Common situations: Postgres failover mid-transaction; a status column CHECK/domain that predates the 'replaced' status value introduced by replacement support; migrations that added the current flag not applied.
Related errors
- Failed to mark execution intent replaced: {e}
- Failed to update execution hash {transaction_hash}: {e}
- Failed to update execution intent {intent_id}: {e}
- Failed to record execution transition: {e}
- Failed to start replacement transaction persistence: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21).
Data as JSON: /api/errors/df032ee44d443de9.
Report an issue: GitHub.