Hmbown/CodeWhale · error · anyhow::Error

CRON {field_name} value {value} is out of range {min}-{max}

Error message

CRON {field_name} value {value} is out of range {min}-{max}

What it means

parse_cron_atom resolves each token via the field's name map (month and day-of-week names) or as u32, then enforces inclusive bounds: minute 0-59, hour 0-23, day-of-month 1-31, month 1-12, day-of-week 0-7 (7 normalizes to 0 = Sunday). Out-of-bound numbers such as minute 60, hour 24, month 0 or 13, and weekday 8 bail here.

Source

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

        self.0
            .iter()
            .find_map(|(name, value)| (*name == needle).then_some(*value))
    }
}

fn parse_cron_atom(
    raw: &str,
    min: u32,
    max: u32,
    names: CronNameMap,
    field_name: &str,
) -> Result<u32> {
    let value = names
        .lookup(raw)
        .or_else(|| raw.parse::<u32>().ok())
        .ok_or_else(|| anyhow::anyhow!("Invalid CRON {field_name} value '{raw}'"))?;
    if !(min..=max).contains(&value) {
        bail!("CRON {field_name} value {value} is out of range {min}-{max}");
    }
    Ok(value)
}

fn weekday_to_cron(day: Weekday) -> u32 {
    match day {
        Weekday::Sun => 0,
        Weekday::Mon => 1,
        Weekday::Tue => 2,
        Weekday::Wed => 3,
        Weekday::Thu => 4,
        Weekday::Fri => 5,
        Weekday::Sat => 6,
    }
}

fn days_in_month(year: i32, month: u32) -> u32 {
    match month {

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Use 0 for midnight (hours are 0-23, minutes 0-59)
  2. Use weekday 0 or 7 for Sunday, 1-6 for Monday-Saturday
  3. Day-of-month starts at 1 and month at 1
  4. Dry-run the EXPR via parse_rrule

Example fix

// before (there is no hour 24)
rrule = "FREQ=CRON;EXPR=0 24 * * *"

// after (midnight)
rrule = "FREQ=CRON;EXPR=0 0 * * *"
Defensive patterns

Strategy: validation

Validate before calling

const CRON_BOUNDS: [(u32, u32); 5] = [(0, 59), (0, 23), (1, 31), (1, 12), (0, 7)];

fn cron_values_in_bounds(expr: &str) -> bool {
    let fields: Vec<&str> = expr.split_whitespace().collect();
    fields.len() == 5
        && fields.iter().enumerate().all(|(i, f)| {
            let (min, max) = CRON_BOUNDS[i];
            f.split(',').all(|item| {
                item.split('/').next().unwrap_or(item)
                    .split('-')
                    .all(|atom| atom.trim().parse::<u32>().is_ok_and(|v| v >= min && v <= max))
            })
        })
}

Prevention

When it happens

Trigger: EXPR='60 * * * *'; '0 24 * * *'; '* * 0 * *' (day-of-month 0); '0 0 * 13 *'; '0 12 * * 8'.

Common situations: Off-by-one ports (24 for midnight, 60 for top of hour); assuming 1-indexed weekdays; typos in numeric fields.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/f53787383c3c4d40. Report an issue: GitHub.