nautechsystems/nautilus_trader · error · anyhow::Error

Execution transaction {transaction_hash} was not found for s

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.

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)

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.