Hmbown/CodeWhale · error · anyhow::Error

CRON field must not be empty

Error message

CRON {field_name} field must not be empty

What it means

CronField::parse rejects a field whose trimmed text is empty. Every cron expression must supply a non-empty value for each of the minute/hour/day/month/weekday fields, and an empty field would produce a field with no values. The {field_name} placeholder is interpolated with the actual field name.

Solutions

  1. Provide an explicit value for the empty field (use * to mean 'every')
  2. Check the source of the cron string — an env var or config key feeding the field is unset or empty
  3. Trim and re-validate the whole expression before passing it in

Example fix

// before
let expr = format!("{} * * * *", maybe_empty); // empty minute field
// after
let minute = if maybe_empty.is_empty() { "*" } else { &maybe_empty };
let expr = format!("{} * * * *", minute);
Defensive patterns

Strategy: validation

Validate before calling

fn valid_cron(expr: &str) -> bool {
    let fields: Vec<&str> = expr.split_whitespace().collect();
    fields.len() == 5 && fields.iter().all(|f| !f.trim().is_empty())
}

Prevention

When it happens

Trigger: Parsing a cron expression with an empty component, e.g. "30 * * * " (trailing empty weekday field), " 9 * * * *" is fine but "" for any field after splitting, or programmatic construction like format!("{} * * * *", empty_var).

Common situations: Template/variable interpolation where a config value is unset; copy-paste losing the last field; splitting an expression on whitespace and rejoining incorrectly.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

        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 == "*";
        for part in trimmed.split(',') {
            let part = part.trim();
            if part.is_empty() {
                bail!("CRON {field_name} field contains an empty list item");
            }
            let (base, step) = if let Some((base, step)) = part.split_once('/') {
                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 {

View on GitHub (pinned to 73e0f67d83)