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(&current_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

  1. Downcast to sqlx::Error and read the database error to identify constraint vs connectivity causes
  2. Apply migrations so execution_transaction_hash has current, status (with 'replaced' allowed), and updated_at
  3. Retry the whole add_execution_replacement_hash call for transient classes - the transaction rolled back, so nothing was retired
  4. 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

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


AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21). Data as JSON: /api/errors/df032ee44d443de9. Report an issue: GitHub.