nautechsystems/nautilus_trader · error
Retained terminal receipt is not on the verified finalized a
Error message
Retained terminal receipt is not on the verified finalized ancestry
What it means
During a migration of terminal execution receipts, the client verifies that a retained terminal receipt actually sits on the chain's verified finalized header ancestry. It fetches an inclusion header via the verification service and checks that the header hash matches the receipt's block hash and that a finalized header with the same number/hash exists in the fetched ancestry. This guards against retaining receipts for orphaned or reorged-out blocks as the terminal state.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:5513
format!(
"Retained transaction hash {} is invalid",
current.transaction_hash
)
},
)?)
.await,
"migration receipt",
)?;
let receipt = receipt_verification.value.clone();
anyhow::ensure!(
receipt.block_number <= finalized.number,
"Retained terminal receipt is above the verified finalized boundary"
);
let inclusion_verification = required_verification(
self.verification.verify_block(receipt.block_number).await,
"migration inclusion header",
)?;
anyhow::ensure!(
inclusion_verification.value.hash == receipt.block_hash
&& finalized_headers.iter().any(|header| {
header.number == receipt.block_number
&& header.hash == receipt.block_hash.to_string()
}),
"Retained terminal receipt is not on the verified finalized ancestry"
);
let tx_hash = B256::from_str(¤t.transaction_hash)
.context("Retained transaction hash is invalid")?;
let included = IncludedTransaction {
intent_id: intent.id,
nonce,
tx_hash,
block_number: receipt.block_number,
receipt: receipt.clone(),
finality: StableFinality {
decisions: Vec::new(),
inclusion_header: durable_verified_header(&inclusion_verification.value),View on GitHub (pinned to 18893faf8b)
Solutions
- Verify the receipt's block_hash/number against a trusted RPC/explorer and confirm the block is finalized on the current chain
- Purge or re-derive the retained terminal receipt from durable storage so migration re-fetches a valid one
- Ensure the verification service/endpoint points at the same chain (correct network_id/chain id) as the receipt
- Re-run the migration after the node has fully caught up and finalized blocks
Example fix
// before (stale/orphaned receipt in durable store)
receipt = ExecutionReceipt { block_number: 1001, block_hash: 0xaaa... }
// after (re-derived from finalized chain)
receipt = fetch_finalized_receipt(block_number: 1001) // hash now matches finalized ancestry 0xbbb... Defensive patterns
Strategy: validation
Validate before calling
// before running migration, confirm the receipt block is finalized on the current chain let header = verification.verify_block(receipt.block_number).await?; assert_eq!(header.value.hash, receipt.block_hash, "receipt block not on current chain"); assert!(header.value.status.is_finalized(), "receipt block not finalized");
Type guard
fn is_on_finalized_ancestry(receipt: &Receipt, headers: &[Header]) -> bool {
headers.iter().any(|h| h.number == receipt.block_number && h.hash == receipt.block_hash.to_string())
} Try / catch
match run_migration().await {
Err(e) if e.to_string().contains("not on the verified finalized ancestry") => {
purge_stale_receipt(&receipt_id)?; // re-derive from finalized chain
}
Err(e) => return Err(e),
Ok(r) => Ok(r),
} Prevention
- Verify receipts against a trusted finalized block source before persisting them as terminal
- Pin the verification endpoint to the same chain/network as the receipts
- Watch for reorgs (hash changes at a given height) and invalidate stored receipts when detected
- Never hand-edit durable receipt records; use the client's repair paths
When it happens
Trigger: Running the receipt-migration path where a stored terminal receipt references a block_hash/number that is either not on the finalized ancestry (orphaned block, chain reorg) or where verify_block returns a header whose hash no longer matches the stored block_hash.
Common situations: A node that followed a minority fork or was reorged; a receipt persisted before a deep reorg; corrupted or hand-edited durable state; running the migration against a different network than the one that produced the receipt.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Durable finalized header tip precedes the trusted checkpoint
- Finalized execution transaction {tx_hash} no longer has a re
- Finalized block {} changed from {} to {} before intent valid
- Finalized block {} changed from {} to {} before fill emissio
- Verified finalized transaction count advanced without an act
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1db45a76830ae5b0.
Report an issue: GitHub.