nautechsystems/nautilus_trader · error · anyhow::Error

Execution transaction

Error message

Execution transaction {transaction_hash} was not found for status update

What it means

This error is raised when an UPDATE on the `execution_transaction` table affected zero rows, meaning no row exists for the given transaction hash. The library enforces `rows_affected() == 1` after the status-update statement to guarantee the caller only advances statuses of transactions it previously persisted. It is an existence guard against silently updating nothing.

Solutions

  1. Verify the transaction hash was previously inserted into execution_transaction for the same chain_id before updating its status
  2. Check whether a retention/cleanup job deleted the row while the status update was in flight
  3. Confirm you are connected to the same database/schema the transaction was written to
  4. If the transaction may legitimately be absent, check existence first with a SELECT and skip the update instead of updating blindly

Example fix

// before
update_status(&db, &missing_hash, Status::Confirmed).await?;
// after
if db.execution_transaction_exists(chain_id, &missing_hash).await? {
    update_status(&db, &missing_hash, Status::Confirmed).await?;
} else {
    tracing::warn!(hash=%missing_hash, "skipping status update for unknown execution transaction");
}
Defensive patterns

Strategy: validation

Validate before calling

let exists: bool = sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM execution_transaction WHERE chain_id = $1 AND transaction_hash = $2)")
    .bind(chain_id).bind(&tx_hash).fetch_one(&pool).await?;
if !exists { skip_or_insert(); }

Prevention

When it happens

Trigger: Calling the transaction status update method with a transaction_hash that was never inserted into execution_transaction, or updating a row that was deleted concurrently.

Common situations: A consumer tracks a transaction hash from a different chain/database than the cache DB being updated; the row was pruned by retention/cleanup before the status callback arrived; a hash is passed from an unverified event source; replaying events after a database reset.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at crates/adapters/blockchain/src/cache/database.rs:7411

        chain_id: u32,
        transaction_hash: &str,
        status: &str,
    ) -> anyhow::Result<()> {
        let result = sqlx::query(
            "
            UPDATE execution_transaction
            SET status = $3
            WHERE chain_id = $1 AND transaction_hash = $2
        ",
        )
        .bind(chain_id as i32)
        .bind(transaction_hash)
        .bind(status)
        .execute(&self.pool)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to update execution_transaction table: {e}"))?;

        anyhow::ensure!(
            result.rows_affected() == 1,
            "Execution transaction {transaction_hash} was not found for status update"
        );
        Ok(())
    }

    /// Loads an execution transaction record by chain ID and transaction hash.
    ///
    /// # Errors
    ///
    /// Returns an error if the database operation fails.
    pub async fn get_execution_transaction(
        &self,
        chain_id: u32,
        transaction_hash: &str,
    ) -> anyhow::Result<Option<ExecutionTransactionRow>> {
        let chain_id_db = i32::try_from(chain_id)
            .with_context(|| format!("Chain ID {chain_id} exceeds PostgreSQL INTEGER"))?;

View on GitHub (pinned to 18893faf8b)