nautechsystems/nautilus_trader · error

Fetched block {} while requesting RPC snapshot block {}

Error message

Fetched block {} while requesting RPC snapshot block {}

What it means

block_scoped_snapshot_position_from_block checks that the Block object it received has the same number as the block_number that was requested when building an RPC snapshot. A mismatch means the caller fetched (or was handed) the wrong block for the requested snapshot position. The library bails to prevent constructing a BlockPosition from inconsistent data.

Source

Thrown at crates/adapters/blockchain/src/data/core.rs:2300

        let block = blocks_stream
            .next()
            .await
            .with_context(|| format!("failed to fetch block {block_number} for RPC snapshot"))?;

        let block_position =
            Self::block_scoped_snapshot_position_from_block(&mut self.cache, &block, block_number)?;
        self.cache.add_pool_event_blocks_batch(vec![block]).await?;

        Ok(block_position)
    }

    fn block_scoped_snapshot_position_from_block(
        cache: &mut BlockchainCache,
        block: &Block,
        block_number: u64,
    ) -> anyhow::Result<BlockPosition> {
        if block.number != block_number {
            anyhow::bail!(
                "Fetched block {} while requesting RPC snapshot block {}",
                block.number,
                block_number
            );
        }

        cache.cache_block_metadata(block);

        Ok(BlockPosition::new(
            block.number,
            block.hash.clone(),
            BLOCK_SCOPED_SNAPSHOT_INDEX,
            BLOCK_SCOPED_SNAPSHOT_INDEX,
        )
        .with_block_hash(Some(block.hash.clone())))
    }

    /// Replays historical events for a pool to hydrate its profiler state.

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Fetch the block and the snapshot in one atomic request at a pinned block number or hash
  2. Assert block equality immediately after fetching and retry with a fresh fetch on mismatch
  3. Use block hashes instead of numbers to be reorg-safe
  4. Avoid reusing cached Block objects across snapshot requests
  5. Serialize snapshot construction so no cache invalidation can swap the block mid-flight

Example fix

// before: reusing possibly stale block
let block = cache.get_cached_block();
let position = block_scoped_snapshot_position_from_block(&mut cache, &block, requested_number)?;
// after: refetch the exact block requested
let block = client.fetch_block(requested_number).await?;
let position = block_scoped_snapshot_position_from_block(&mut cache, &block, requested_number)?;
Defensive patterns

Strategy: validation

Validate before calling

if block.number != block_number {
    return Err(anyhow!("block mismatch: got {}, wanted {}", block.number, block_number));
}

Type guard

fn is_requested_block(block: &Block, n: u64) -> bool { block.number == n }

Try / catch

match build_snapshot_position(&mut cache, &block, n) {
    Ok(pos) => pos,
    Err(e) if e.to_string().contains("Fetched block") => retry_with_fresh_fetch(n).await?,
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling the snapshot-position code path when block.number != block_number — typically after a helper returns a 'latest' block that has advanced past the requested number, or a caller passes a cached block from an earlier fetch alongside a newer block number.

Common situations: Race with block production: the node advanced between requesting the block number and fetching its body; a stale cached Block object reused with a fresh block_number; a reorg replacing the block at that height; passing block.number from a log instead of the snapshot request block.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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