nautechsystems/nautilus_trader · error

Replacement scan tip conflicts with the verified canonical h

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.

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)

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.