Hmbown/CodeWhale · error

ONCE schedules require AT

Error message

ONCE schedules require AT

What it means

AutomationSchedule::parse_rrule treats FREQ=ONCE as a one-shot: only FREQ and AT are allowed, and the AT key is mandatory because a one-shot without a fire time is meaningless. A missing AT is rejected with this error at parse time, before the automation is stored.

Source

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

            "SU" => Weekday::Sun,
            other => bail!("Invalid BYDAY value '{other}'"),
        };
        if !days.contains(&day) {
            days.push(day);
        }
    }
    Ok(days)
}

fn parse_once_schedule(parts: &BTreeMap<String, String>) -> Result<AutomationSchedule> {
    for key in parts.keys() {
        if key != "FREQ" && key != "AT" {
            bail!("Unsupported RRULE field '{key}' for ONCE. Allowed: FREQ,AT");
        }
    }
    let raw_at = parts
        .get("AT")
        .ok_or_else(|| anyhow::anyhow!("ONCE schedules require AT"))?;
    let at = parse_once_at(raw_at)?;
    Ok(AutomationSchedule::Once { at })
}

fn parse_cron_schedule(parts: &BTreeMap<String, String>) -> Result<AutomationSchedule> {
    for key in parts.keys() {
        if key != "FREQ" && key != "EXPR" {
            bail!("Unsupported RRULE field '{key}' for CRON. Allowed: FREQ,EXPR");
        }
    }
    let expr = parts
        .get("EXPR")
        .map(|value| value.trim().to_string())
        .filter(|value| !value.is_empty())
        .ok_or_else(|| anyhow::anyhow!("CRON schedules require EXPR"))?;
    ParsedCronExpr::parse(&expr)?;
    Ok(AutomationSchedule::Cron { expr })
}

View on GitHub (pinned to 8880682c63)

Solutions

  1. Append AT with a local or RFC3339 time: FREQ=ONCE;AT=2026-08-03T14:30 or FREQ=ONCE;AT=2026-08-03T12:30:00Z
  2. Use exactly the key AT and no other fields (only FREQ and AT are allowed for ONCE)
  3. Validate with parse_rrule before persisting

Example fix

// before
let rrule = "FREQ=ONCE";
// after
let rrule = "FREQ=ONCE;AT=2026-08-03T14:30";
Defensive patterns

Strategy: validation

Validate before calling

let parts: std::collections::BTreeMap<&str, &str> = rrule
    .split(';')
    .filter_map(|kv| kv.split_once('='))
    .collect();
if parts.get("FREQ").copied() == Some("ONCE") {
    let at = parts.get("AT").context("ONCE schedules require AT=YYYY-MM-DDTHH:MM[:SS] or RFC3339")?;
    ensure!(!at.trim().is_empty(), "AT must not be empty");
}

Type guard

fn is_valid_once_rrule(rrule: &str) -> bool {
    let parts: std::collections::BTreeMap<&str, &str> = rrule
        .split(';')
        .filter_map(|kv| kv.split_once('='))
        .collect();
    parts.get("FREQ").copied() == Some("ONCE")
        && parts.get("AT").is_some_and(|at| !at.trim().is_empty())
        && parts.keys().all(|k| matches!(*k, "FREQ" | "AT"))
}

Prevention

When it happens

Trigger: Creating/updating an automation with rrule 'FREQ=ONCE' (no ';AT=...'), or with the field misspelled so the parser sees no AT key. AT accepts local 'YYYY-MM-DDTHH:MM[:SS]' or RFC3339.

Common situations: Authors copying the ONCE template but deleting the AT clause; model-generated schedules omitting the timestamp; field-name typos like 'AT ' or 'AT=' (empty value fails later in parse_once_at instead).

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/8825f993391f9d1c. Report an issue: GitHub.