Hmbown/CodeWhale · error · anyhow::Error

CRON EXPR day-of-month/month combination can never occur

Error message

CRON EXPR day-of-month/month combination can never occur

What it means

validate_date_space runs after field parsing when day-of-month is not a wildcard: for each listed month it checks that at least one listed day is <= that month's day count in 2024 (leap) or 2025 (common). If no month/day pair can ever exist, the expression is rejected as unmatchable. '0 0 29 2 *' passes because Feb 29 exists in leap years; '0 0 30 2 *' and '0 0 31 4 *' fail.

Source

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

            day_of_month || weekday
        }
    }

    fn validate_date_space(&self) -> Result<()> {
        if self.day_of_month.is_wildcard {
            return Ok(());
        }
        let months = self.month.values();
        let days = self.day_of_month.values();
        let valid = months.iter().copied().any(|month| {
            let common = days_in_month(2025, month);
            let leap = days_in_month(2024, month);
            days.iter().copied().any(|day| day <= common || day <= leap)
        });
        if valid {
            Ok(())
        } else {
            bail!("CRON EXPR day-of-month/month combination can never occur")
        }
    }
}

#[derive(Debug, Clone)]
struct CronField {
    values: Vec<u32>,
    is_wildcard: bool,
}

impl CronField {
    fn parse(raw: &str, min: u32, max: u32, names: CronNameMap, field_name: &str) -> Result<Self> {
        let trimmed = raw.trim();
        if trimmed.is_empty() {
            bail!("CRON {field_name} field must not be empty");
        }
        let mut values = Vec::new();
        let is_wildcard = trimmed == "*";

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Restrict the month field to months that have the day, e.g. '0 0 31 1,3,5,7,8,10,12 *'
  2. Or use a range like 28-31 and verify the month length inside the task
  3. Remember February contributes at most 29; Feb-29-only rules then face the 5-year search limit
  4. Dry-run parse_rrule to catch this at configuration time

Example fix

// before (April 31 does not exist)
rrule = "FREQ=CRON;EXPR=0 0 31 4 *"

// after (31st only in 31-day months)
rrule = "FREQ=CRON;EXPR=0 0 31 1,3,5,7,8,10,12 *"
Defensive patterns

Strategy: validation

Validate before calling

fn cron_expr_valid(expr: &str) -> bool {
    AutomationSchedule::parse_rrule(&format!("FREQ=CRON;EXPR={expr}")).is_ok()
}

Prevention

When it happens

Trigger: EXPR='0 0 31 4 *' (April 31); '0 0 30 2 *' (Feb 30); '0 0 31 4,6,9,11 *' (day 31 restricted to 30-day months).

Common situations: End-of-month crontabs applied blindly to every month; generated expressions enumerating day 31 for monthly tasks.

Related errors


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