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
- Re-fetch the head and retry the scan after the chain stabilizes
- Pin to a single consistent RPC endpoint for head and window queries
- Persist/re-verify the head header before comparing with the scan tip
- 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
- Re-verify the head immediately before comparison
- Avoid switching RPC nodes mid-scan
- Add reorg detection before reconciliation
- Delay scans near the head until blocks are finalized
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
- Replacement block conflicts with its canonical header
- Canonical nonce advanced without an authenticated signer…
- Finalized Swap log position does not match transaction
- Profiler log position does not match its ingestion watermark
- Profiler receipt position does not match its ingestion…
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
.databaseView on GitHub (pinned to 18893faf8b)