nautechsystems/nautilus_trader · error

Replacement scan conflicts with the durable finalized header

Error message

Replacement scan conflicts with the durable finalized header ledger

What it means

After the replacement scan, the scanned tip must match the durable finalized header cursor stored in the database (if one exists for this intent/nonce/manifest). A mismatch means the in-memory scan result contradicts the persisted finalized ledger — the local scan and durable history have diverged.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:3143

            });
        let candidate = candidates.next();
        anyhow::ensure!(
            candidates.next().is_none(),
            "Canonical replacement scan found duplicate signer-nonce transactions"
        );

        let finalized_cursor = self
            .database
            .load_execution_verified_header(
                self.chain_id,
                &wallet_address,
                end,
                &self.manifest_digest,
            )
            .await?;

        if let Some(cursor) = finalized_cursor.as_ref() {
            anyhow::ensure!(
                parse_verified_header(cursor)? == scanned_tip,
                "Replacement scan conflicts with the durable finalized header ledger"
            );
        }

        let mut mismatch = None;
        let matched = candidate.and_then(|transaction| {
            let Some(raw_transaction) = authenticated_payloads.get(&transaction.hash).cloned()
            else {
                mismatch = Some(anyhow::anyhow!(
                    "Canonical signer-nonce transaction {} has no authenticated retained payload",
                    transaction.hash
                ));
                return None;
            };

            if let Err(e) = validate_rpc_transaction_matches_payload(transaction, &raw_transaction)
            {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm chain_id, wallet, and manifest_digest in the client config match those the ledger cursor was written with
  2. Inspect the execution_replacement_cursor row and compare its header with the chain; reorg-aware repair or reset the cursor
  3. Re-run reconciliation from an earlier block so the ledger is rebuilt consistently
  4. Verify no out-of-band process mutates the ledger concurrently
Defensive patterns

Strategy: validation

Validate before calling

let cursor = db.load_execution_replacement_cursor(intent.id, chain_id, wallet, nonce, manifest_digest).await?;
if let Some(cur) = cursor {
    if parse_verified_header(&cur)? != scanned_tip {
        return Err("ledger cursor and scan diverged; repair before proceeding");
    }
}

Prevention

When it happens

Trigger: load_execution_replacement_cursor returned a finalized cursor whose parsed header differs from the scanned tip — e.g. the database cursor was written against a different chain/manifest, or a reorg invalidated previously finalized data while the DB retains the old header.

Common situations: Reusing a database across chain environments; reorg after the cursor was persisted; manually edited or migrated DB rows; pointing the client at a different RPC after the ledger was built.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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