nautechsystems/nautilus_trader · error
Verified replacement scan returned no blocks
Error message
Verified replacement scan returned no blocks
What it means
After assembling the block list for a canonical replacement scan, the client requires at least one verified block so it can derive a scanned tip. The verified replacement scan (window or start block fetch) produced an empty block list, which is an unexpected provider/indexer response.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:3113
));
blocks.push(start_block.value);
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"
);
View on GitHub (pinned to 18893faf8b)
Solutions
- Retry with a different or archive-capable RPC/indexer provider
- Verify the provider returns blocks for the requested range (query a known height directly)
- Check that start <= end in the computed scan range and that the cursor is valid
- Re-run reconciliation once the provider recovers
Defensive patterns
Strategy: validation
Validate before calling
let blocks = client.verified_window(start, end).await?;
if blocks.is_empty() {
return Err("provider returned no blocks for range; check provider health/archive support");
} Try / catch
match result {
Err(e) if e.to_string().contains("returned no blocks") => switch_provider_and_retry(),
other => other,
} Prevention
- Use archive-capable providers for historical scans
- Health-check the verification provider before reconciliation
- Validate requested ranges are non-degenerate
- Keep a fallback provider configured
When it happens
Trigger: verify_replacement_window() or verify_replacement_block() returned an empty result while the computed scan range start..=end was valid — e.g. the verification provider returned no blocks for the requested window, or the cursor/window logic produced a degenerate range.
Common situations: Indexer/RPC with gaps in historical block availability; archive provider missing recently pruned blocks; a verification backend returning an empty payload on partial outages.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- {context} verification disagreed
- {context} verification is locally invalid
- Receipt verification is locally invalid for transaction {tx_
- Finalized header verification disagreed
- Finalized header verification is locally invalid
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1179f8370661c7e6.
Report an issue: GitHub.