nautechsystems/nautilus_trader · error

Replacement scan tip conflicts with the verified canonical…

Error message

Replacement scan tip conflicts with the verified canonical head

What it means

When the replacement scan reaches the current verified head (end == head.number), the tip of the scanned block list must exactly equal the already-verified canonical head header. A mismatch means the scanned replacement history and the verified canonical head disagree — evidence of a reorg or an inconsistent data source.

Solutions

  1. Re-fetch the head and retry the scan after the chain stabilizes
  2. Pin to a single consistent RPC endpoint for head and window queries
  3. Persist/re-verify the head header before comparing with the scan tip
  4. Investigate for a deep reorg if mismatches persist
Defensive patterns

Strategy: retry

Validate before calling

let head = client.verified_head().await?;
let tip = scanned_blocks.last().map(VerifiedBlockHeader::from);
if end == head.number && tip != Some(head) {
    return Err("scan tip diverged from head; re-fetch and retry");
}

Try / catch

match result {
    Err(e) if e.to_string().contains("conflicts with the verified canonical head") => {
        refetch_head_and_retry_with_backoff()
    }
    other => other,
}

Prevention

When it happens

Trigger: The last block returned by verify_replacement_window() has a different header than the verified head used to compute the scan end, i.e. the head moved (reorg) or the window fetch returned blocks from another branch.

Common situations: Chain reorg between fetching head and scanning; RPC load balancer serving different branches; stale cached head from the verification provider.

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/d37d7fc68bf1d6cd. Report an issue: GitHub.

Appendix: source

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

            if end > start {
                let window = required_verification(
                    self.verification
                        .verify_replacement_window(start_header.value, end)
                        .await,
                    "canonical replacement window",
                )?;
                decisions.push(verification_decision(&window, Some(start + 1), Some(end)));
                blocks.extend(window.value);
            }
        }

        let scanned_tip = blocks
            .last()
            .map(|block| VerifiedBlockHeader::from(block.clone()))
            .ok_or_else(|| anyhow::anyhow!("Verified replacement scan returned no blocks"))?;
        if end == head.number {
            anyhow::ensure!(
                scanned_tip == head,
                "Replacement scan tip conflicts with the verified canonical head"
            );
        }
        let mut candidates = blocks
            .iter()
            .flat_map(|block| block.transactions.iter())
            .filter(|transaction| {
                transaction.from == self.wallet_address && transaction.nonce == nonce
            });
        let candidate = candidates.next();
        anyhow::ensure!(
            candidates.next().is_none(),
            "Canonical replacement scan found duplicate signer-nonce transactions"
        );

        let finalized_cursor = self
            .database

View on GitHub (pinned to 18893faf8b)