linera-io/linera-protocol · error

--bulk-height-range must be `auto` or `FROM:TO`

Error message

--bulk-height-range must be `auto` or `FROM:TO`

What it means

The --bulk-height-range value was neither the literal "auto" nor a FROM:TO pair, because no ':' separator was found. resolve_range accepts exactly "auto" (computed as a window below the chain tip) or two u64 numbers split on the first colon.

Source

Thrown at linera-service/src/cli/validator_benchmark/bulk_download.rs:175

        bytes_in,
        certs_received,
        duration_secs: duration,
        mb_per_sec: (bytes_in as f64 / 1_048_576.0) / duration,
        certs_per_sec: certs_received as f64 / duration,
        latency_ms: samples.summary(),
    }
}

/// Resolve the height range: `auto` targets the most recent
/// `batch_size * AUTO_BATCH_COUNT` heights up to the tip; otherwise `FROM:TO`.
fn resolve_range(arg: &str, tip: u64, batch_size: u32) -> Result<(u64, u64)> {
    if arg == "auto" {
        let span = batch_size as u64 * AUTO_BATCH_COUNT;
        Ok((tip.saturating_sub(span), tip))
    } else {
        let (a, b) = arg
            .split_once(':')
            .ok_or_else(|| anyhow::anyhow!("--bulk-height-range must be `auto` or `FROM:TO`"))?;
        let from: u64 = a.trim().parse()?;
        let to: u64 = b.trim().parse()?;
        if to < from {
            anyhow::bail!("--bulk-height-range FROM must not exceed TO");
        }
        Ok((from, to))
    }
}

#[cfg(test)]
mod tests {
    use super::resolve_range;

    #[test]
    fn auto_under_tip() {
        assert_eq!(
            resolve_range("auto", 50_000, 100).unwrap(),
            (40_000, 50_000)

View on GitHub (pinned to 6c226ddcb3)

Solutions

  1. Use the literal `--bulk-height-range auto` to let the tool derive the window from the tip.
  2. Or spell the explicit range with a colon: `--bulk-height-range 1000:2000`.
  3. Ensure FROM <= TO and both are plain decimal u64 values (no units, no 0x).
  4. Check for stray whitespace or a duplicated flag overriding the value.

Example fix

# before
--bulk-height-range 1000-2000

# after
--bulk-height-range 1000:2000   # or: --bulk-height-range auto
Defensive patterns

Strategy: type-guard

Validate before calling

fn is_valid_bulk_height_range(s: &str) -> bool {
    if s == "auto" { return true; }
    match s.split_once(':') {
        Some((a, b)) => {
            let (a, b) = (a.trim().parse::<u64>(), b.trim().parse::<u64>());
            matches!((a, b), (Ok(from), Ok(to)) if from <= to)
        }
        None => false,
    }
}

Type guard

fn is_valid_bulk_height_range(s: &str) -> bool {
    s == "auto" || s.split_once(':').is_some_and(|(a, b)| {
        a.trim().parse::<u64>().is_ok() && b.trim().parse::<u64>().is_ok()
    })
}

Prevention

When it happens

Trigger: Passing "100-200" (dash instead of colon), a bare number "1500", or "auto " with trailing characters that defeat the literal comparison.

Common situations: Muscle memory from other tools that use dash ranges; scripts templating the argument with a wrong separator; also note the follow-up guards: FROM must not exceed TO, and both ends must parse as u64.

Related errors


AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22). Data as JSON: /api/errors/00581bce12ba1648. Report an issue: GitHub.