{"record":{"id":"00581bce12ba1648","repo":"linera-io/linera-protocol","slug":"bulk-height-range-must-be-auto-or-from-to","errorCode":null,"errorMessage":"--bulk-height-range must be `auto` or `FROM:TO`","messagePattern":"--bulk-height-range must be `auto` or `FROM:TO`","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-service/src/cli/validator_benchmark/bulk_download.rs","lineNumber":175,"sourceCode":"        bytes_in,\n        certs_received,\n        duration_secs: duration,\n        mb_per_sec: (bytes_in as f64 / 1_048_576.0) / duration,\n        certs_per_sec: certs_received as f64 / duration,\n        latency_ms: samples.summary(),\n    }\n}\n\n/// Resolve the height range: `auto` targets the most recent\n/// `batch_size * AUTO_BATCH_COUNT` heights up to the tip; otherwise `FROM:TO`.\nfn resolve_range(arg: &str, tip: u64, batch_size: u32) -> Result<(u64, u64)> {\n    if arg == \"auto\" {\n        let span = batch_size as u64 * AUTO_BATCH_COUNT;\n        Ok((tip.saturating_sub(span), tip))\n    } else {\n        let (a, b) = arg\n            .split_once(':')\n            .ok_or_else(|| anyhow::anyhow!(\"--bulk-height-range must be `auto` or `FROM:TO`\"))?;\n        let from: u64 = a.trim().parse()?;\n        let to: u64 = b.trim().parse()?;\n        if to < from {\n            anyhow::bail!(\"--bulk-height-range FROM must not exceed TO\");\n        }\n        Ok((from, to))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::resolve_range;\n\n    #[test]\n    fn auto_under_tip() {\n        assert_eq!(\n            resolve_range(\"auto\", 50_000, 100).unwrap(),\n            (40_000, 50_000)","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-service/src/cli/validator_benchmark/bulk_download.rs#L157-L193","documentation":"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.","triggerScenarios":"Passing \"100-200\" (dash instead of colon), a bare number \"1500\", or \"auto \" with trailing characters that defeat the literal comparison.","commonSituations":"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.","solutions":["Use the literal `--bulk-height-range auto` to let the tool derive the window from the tip.","Or spell the explicit range with a colon: `--bulk-height-range 1000:2000`.","Ensure FROM <= TO and both are plain decimal u64 values (no units, no 0x).","Check for stray whitespace or a duplicated flag overriding the value."],"exampleFix":"# before\n--bulk-height-range 1000-2000\n\n# after\n--bulk-height-range 1000:2000   # or: --bulk-height-range auto","handlingStrategy":"type-guard","validationCode":"fn is_valid_bulk_height_range(s: &str) -> bool {\n    if s == \"auto\" { return true; }\n    match s.split_once(':') {\n        Some((a, b)) => {\n            let (a, b) = (a.trim().parse::<u64>(), b.trim().parse::<u64>());\n            matches!((a, b), (Ok(from), Ok(to)) if from <= to)\n        }\n        None => false,\n    }\n}","typeGuard":"fn is_valid_bulk_height_range(s: &str) -> bool {\n    s == \"auto\" || s.split_once(':').is_some_and(|(a, b)| {\n        a.trim().parse::<u64>().is_ok() && b.trim().parse::<u64>().is_ok()\n    })\n}","tryCatchPattern":null,"preventionTips":["Use the colon separator, not a dash.","Prefer `auto` unless a fixed window is required.","Validate the argument in wrapper scripts before launching the benchmark."],"tags":["cli","parsing","block-height"],"backgroundTag":"invalid-cli-argument-format","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}