nautechsystems/nautilus_trader · error · anyhow::Error
Finality transaction hash was not found
Error message
Finality transaction hash was not found
What it means
An assertion that the UPDATE of `execution_transaction_hash` (status, block_number, block_hash) affected exactly one row. Zero affected rows means no transaction-hash record exists for this finality's transaction hash, so the receipt cannot be attached to a known transaction and the finality commit aborts. It is a referential-integrity check against the transaction-hash ledger.
Source
Thrown at crates/adapters/blockchain/src/cache/database.rs:7131
UPDATE execution_transaction_hash
SET status = $3, block_number = $4, block_hash = $5,
receipt_success = $6, gas_used = $7, effective_gas_price = $8,
updated_at = NOW()
WHERE intent_id = $1 AND transaction_hash = $2
",
)
.bind(finality.intent_id)
.bind(finality.transaction_hash)
.bind(finality.status.as_str())
.bind(block_number)
.bind(finality.block_hash)
.bind(finality.receipt_success)
.bind(gas_used)
.bind(finality.effective_gas_price)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to record verified finality receipt: {e}"))?;
anyhow::ensure!(
hash_result.rows_affected() == 1,
"Finality transaction hash was not found"
);
let active = !fill_emitted && !terminal_emitted;
sqlx::query(
"UPDATE execution_intent SET status = $2, active = $3, updated_at = NOW() WHERE id = $1",
)
.bind(finality.intent_id)
.bind(finality.status.as_str())
.bind(active)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to record verified finality intent: {e}"))?;
let transition_key = format!(
"{}:{}:{}:{}",
finality.status.as_str(),View on GitHub (pinned to 18893faf8b)
Solutions
- Query `execution_transaction_hash` by the finality transaction hash to confirm the row exists and with which chain_id/wallet values
- Ensure ordering: the transaction-hash row must be committed before finality is applied
- Normalize the transaction hash (lowercase hex, consistent 0x handling) on both write and lookup paths
- Check retention/cleanup jobs are not deleting rows pending finality
- Verify the UPDATE's WHERE clause scoping matches how the row was originally written
Example fix
// before
anyhow::ensure!(
hash_result.rows_affected() == 1,
"Finality transaction hash was not found"
);
// after
anyhow::ensure!(
hash_result.rows_affected() == 1,
"Finality transaction hash was not found: hash={} chain={} intent={}",
finality.transaction_hash, chain_id, finality.intent_id
); Defensive patterns
Strategy: validation
Validate before calling
// Confirm the transaction hash row exists before applying finality
let exists = sqlx::query_scalar::<_, i64>(
"SELECT COUNT(*) FROM execution_transaction_hash \
WHERE transaction_hash = $1 AND chain_id = $2 AND wallet_address = $3"
)
.bind(normalize_hash(&finality.transaction_hash))
.bind(chain_id)
.bind(finality.wallet_address)
.fetch_one(&mut *conn).await?;
if exists == 0 { /* defer finality until the hash row is committed */ } Type guard
fn tx_hash_is_known(finality: &VerifiedFinality, known: &HashSet<String>) -> bool {
known.contains(&normalize_hash(&finality.transaction_hash))
} Try / catch
match apply_verified_finality(...).await {
Err(e) if e.to_string().contains("transaction hash was not found") => {
warn!("unknown tx hash {} for finality — deferring", finality.transaction_hash);
requeue(finality);
}
other => other,
} Prevention
- Normalize transaction hashes (lowercase hex, 0x policy) on both write and lookup
- Guarantee the tx-hash row is committed before finality events are consumed
- Exclude rows pending finality from retention/cleanup jobs
- Scope hash rows with the same chain_id/wallet used by finality lookups
When it happens
Trigger: Finality arriving for a transaction hash never recorded in `execution_transaction_hash`; the row was scoped to a different chain_id/wallet in the UPDATE's WHERE clause; a truncated or differently-encoded transaction hash (case/0x-prefix) than stored; the row was deleted by cleanup before finality was processed.
Common situations: Out-of-order processing where finality is applied before the transaction-hash row is written; environment/DB mismatch between the submitter and finality verifier; hash normalization differences (checksummed hex vs lowercase, 0x prefix) after a refactor; retention jobs pruning rows too aggressively.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Active finality intent was not found
- Execution verification manifest identity changed
- Finalized header manifest identity changed
- Active verified-action intent was not found
- Signed transaction {transaction_hash} conflicts with its per
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/c19a690859f8e465.
Report an issue: GitHub.