nautechsystems/nautilus_trader · error · anyhow::Error

All --checkpoint-blocks exceed --to-block {to_block}

Error message

All --checkpoint-blocks exceed --to-block {to_block}

What it means

analyze_pool_with_client normalizes the requested --checkpoint-blocks by clamping them to --to-block. If every requested checkpoint exceeds to_block, the normalized checkpoint list is empty and analysis cannot determine its first replay boundary, so it bails.

Source

Thrown at crates/cli/src/blockchain/analyze.rs:277

    }

    let pool_address = validate_address(&pool_address)?;
    let pool_identifier = PoolIdentifier::Address(Ustr::from(&pool_address.to_string()));

    // Load only this pool into the cache rather than the whole DEX pool set (tens of thousands of
    // pools on large DEXes); sync and profiling below operate on this single pool.
    data_client
        .register_dex_exchange_for_pool(dex_type, &pool_identifier)
        .await
        .map_err(|e| anyhow::anyhow!("Failed to register DEX exchange: {e}"))?;

    let checkpoints = if checkpoint_blocks.is_empty() {
        vec![to_block]
    } else {
        normalize_checkpoints(checkpoint_blocks, to_block)
    };
    let Some(first_checkpoint) = checkpoints.first().copied() else {
        anyhow::bail!("All --checkpoint-blocks exceed --to-block {to_block}");
    };

    // Bounded-replay mode: a usable snapshot must already exist at or before the first checkpoint,
    // otherwise the caller wants needs_bootstrap rather than a full creation-to-target bootstrap.
    if require_existing_snapshot
        && needs_bootstrap_before_target(data_client, &pool_identifier, first_checkpoint).await?
    {
        return Ok(vec![PoolAnalysisOutcome::NeedsBootstrap(
            PoolNeedsBootstrapOutcome {
                pool_address: pool_address.to_string(),
                target_block: first_checkpoint,
            },
        )]);
    }

    let last_checkpoint = checkpoints.last().copied().unwrap_or(first_checkpoint);

    if !snapshot_from_rpc {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Include the target block itself (or checkpoints <= --to-block) in --checkpoint-blocks
  2. Omit --checkpoint-blocks entirely to default to a checkpoint at --to-block
  3. Raise --to-block if you genuinely need checkpoints above the current target
  4. Verify checkpoint values against the chain's current block height

Example fix

// before
nautilus blockchain analyze --to-block 1000000 --checkpoint-blocks 1100000,1200000
// after
nautilus blockchain analyze --to-block 1000000 --checkpoint-blocks 500000,1000000
Defensive patterns

Strategy: validation

Validate before calling

def validate_checkpoints(checkpoints: list[int], to_block: int):
    if checkpoints and all(c > to_block for c in checkpoints):
        raise ValueError("all --checkpoint-blocks exceed --to-block")

Try / catch

try:
    analyze_pool(client, checkpoints, to_block)
except ValueError as e:
    print(e)  # adjust checkpoint-blocks or to-block

Prevention

When it happens

Trigger: Passing --checkpoint-blocks 5000000 with --to-block 4000000; passing only checkpoints above to_block so `checkpoints.first()` is None after normalize_checkpoints clamps/drops them.

Common situations: Reusing an old command line after advancing to a lower --to-block; off-by-one when computing to_block as 'latest - N'; copy-pasted checkpoint values from a different chain with higher block numbers.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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