nautechsystems/nautilus_trader · error · anyhow::Error
Execution transaction hash
Error message
Execution transaction hash {transaction_hash} was not found for intent {intent_id} What it means
Raised by anyhow::ensure! when the execution-hash UPDATE affected 0 rows, meaning no execution_transaction (or equivalent) row exists with the given transaction_hash for this intent. The status lock succeeded but the receipt references a hash that was never recorded, so the transition is rolled back.
Solutions
- Confirm the stored hash: SELECT transaction_hash ... WHERE intent_id = '<id>'; compare exact bytes/casing with the one passed in.
- Persist the execution hash before subscribing to/processing its receipt; enforce ordering in the adapter pipeline.
- Handle replaced transactions: update the intent's hash when a replacement tx is submitted, before the receipt arrives.
- Normalize hash format (consistent lowercase hex) at both write and lookup points.
Example fix
// before
anyhow::ensure!(
hash_result.rows_affected() == 1,
"Execution transaction hash {transaction_hash} was not found for intent {intent_id}"
);
// after
if hash_result.rows_affected() == 0 {
tracing::warn!(%intent_id, %transaction_hash, "unknown execution hash; registering receipt out-of-band");
anyhow::bail!("Execution transaction hash {transaction_hash} was not found for intent {intent_id}");
} Defensive patterns
Strategy: validation
Validate before calling
// Confirm the execution hash is registered before processing its receipt
let known = sqlx::query_scalar::<_, bool>(
"SELECT EXISTS(SELECT 1 FROM execution_transaction WHERE intent_id = $1 AND transaction_hash = $2)")
.bind(&intent_id)
.bind(&transaction_hash)
.fetch_one(&pool)
.await?;
if !known { register_execution_hash(&intent_id, &transaction_hash).await?; } Try / catch
if let Err(e) = transition.await {
if e.to_string().contains("was not found for intent") {
tracing::warn!(%intent_id, %transaction_hash, "hash not registered; check replacement tx or ordering");
}
} Prevention
- Persist the tx hash before subscribing to its receipt events.
- Update the intent's hash whenever a replacement (speed-up/cancel) tx is submitted.
- Normalize hashes to lowercase hex at both write and lookup paths.
When it happens
Trigger: Calling the transition with a transaction_hash that differs from the one stored when the intent was created — e.g. a replacement/ Speedup transaction with a different hash, a hash casing/format mismatch, or the tx was never registered before its receipt was processed.
Common situations: Transaction replaced/repriced (speed-up/cancel) onchain so the mined hash differs from the submitted one; processing receipts before the execute call persisted the hash; mixing lowercase/0x-prefixed hash formats between the record and lookup paths; data loss or reset between registration and receipt.
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
- Active verified-action intent was not found
- Execution intent cannot mark emitted
- Execution intent has no current hash
- Execution intent is not prepared for nonce
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1d3ec55b385f117b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/adapters/blockchain/src/cache/database.rs:6830
receipt_success = COALESCE($6, receipt_success),
gas_used = COALESCE($7, gas_used),
effective_gas_price = COALESCE($8, effective_gas_price),
updated_at = NOW()
WHERE intent_id = $1 AND transaction_hash = $2
",
)
.bind(intent_id)
.bind(transaction_hash)
.bind(status.as_str())
.bind(block_number_db)
.bind(block_hash)
.bind(receipt_success)
.bind(gas_used_db)
.bind(effective_gas_price)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to update execution hash {transaction_hash}: {e}"))?;
anyhow::ensure!(
hash_result.rows_affected() == 1,
"Execution transaction hash {transaction_hash} was not found for intent {intent_id}"
);
sqlx::query(
"
UPDATE execution_intent
SET status = $2, active = $3, updated_at = NOW()
WHERE id = $1
",
)
.bind(intent_id)
.bind(status.as_str())
.bind(active)
.execute(&mut *transaction)
.await
.map_err(|e| anyhow::anyhow!("Failed to update execution intent {intent_id}: {e}"))?;
View on GitHub (pinned to 18893faf8b)