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

  1. Retry with a different or archive-capable RPC/indexer provider
  2. Verify the provider returns blocks for the requested range (query a known height directly)
  3. Check that start <= end in the computed scan range and that the cursor is valid
  4. 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

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


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