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(&current.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

  1. Verify the receipt's block_hash/number against a trusted RPC/explorer and confirm the block is finalized on the current chain
  2. Purge or re-derive the retained terminal receipt from durable storage so migration re-fetches a valid one
  3. Ensure the verification service/endpoint points at the same chain (correct network_id/chain id) as the receipt
  4. 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

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


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