Hmbown/CodeWhale · error · anyhow::Error

CRON range start must be <= end

Error message

CRON {field_name} range start must be <= end

What it means

In a range like '10-5', the start must be less than or equal to the end after both atoms are resolved (including named lookups such as month or weekday names). Descending ranges are not supported, so 'FRI-MON' or '5-1' fails with the field name interpolated.

Solutions

  1. Swap the endpoints so start <= end, e.g. '5-10' instead of '10-5'
  2. For wrap-around ranges (e.g. FRI-MON), express as two lists/ranges: '5-6,0-1' or '5,6,0,1'
  3. Check named atom resolution — verify the field's numbering (cron weekday 0=Sunday) matches your intent

Example fix

// before
"0 0 * * 6-1" // start 6 > end 1
// after
"0 0 * * 0,6" // Sat and Sun
Defensive patterns

Strategy: validation

Validate before calling

fn range_ok(start: u32, end: u32) -> bool { start <= end }

Prevention

When it happens

Trigger: A field part with '-' whose parsed start exceeds its end, e.g. '0 0 * * 6-1' (Sat-Sun in cron is 6,0 — not 6-1) or '10-5' in minutes.

Common situations: Assuming cron ranges wrap around like 'FRI-MON'; writing human-style '20-10' meaning 8pm-10am (needs two ranges); name-to-number confusion where the name resolves higher than expected.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/240b2b2475adc9c4. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/automation_manager.rs:850

                let step = step
                    .trim()
                    .parse::<u32>()
                    .with_context(|| format!("Failed to parse CRON {field_name} step"))?;
                if step == 0 {
                    bail!("CRON {field_name} step must be >= 1");
                }
                (base.trim(), step)
            } else {
                (part, 1)
            };

            let range = if base == "*" {
                (min, max)
            } else if let Some((start, end)) = base.split_once('-') {
                let start = parse_cron_atom(start.trim(), min, max, names, field_name)?;
                let end = parse_cron_atom(end.trim(), min, max, names, field_name)?;
                if start > end {
                    bail!("CRON {field_name} range start must be <= end");
                }
                (start, end)
            } else {
                let start = parse_cron_atom(base, min, max, names, field_name)?;
                if part.contains('/') {
                    (start, max)
                } else {
                    (start, start)
                }
            };

            let mut current = range.0;
            while current <= range.1 {
                if !values.contains(&current) {
                    values.push(current);
                }
                let Some(next) = current.checked_add(step) else {
                    break;

View on GitHub (pinned to 73e0f67d83)