Hmbown/CodeWhale · error · anyhow::Error

Unsupported RRULE field '{key}' for WEEKLY. Allowed: FREQ,BY

Error message

Unsupported RRULE field '{key}' for WEEKLY. Allowed: FREQ,BYDAY,BYHOUR,BYMINUTE

What it means

For FREQ=WEEKLY the parser whitelists exactly FREQ, BYDAY, BYHOUR, BYMINUTE — INTERVAL is deliberately not allowed because WEEKLY fires on the named weekdays at the fixed BYHOUR:BYMINUTE local time. Any other key (INTERVAL, UNTIL, COUNT, EXPR, ...) is rejected rather than ignored.

Source

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

                    .transpose()
                    .context("Failed to parse BYMINUTE")?;
                if anchor_hour.is_some_and(|hour| hour > 23) {
                    bail!("BYHOUR must be between 0 and 23");
                }
                if anchor_minute.is_some_and(|minute| minute > 59) {
                    bail!("BYMINUTE must be between 0 and 59");
                }
                Ok(Self::Hourly {
                    interval_hours,
                    byday,
                    anchor_hour,
                    anchor_minute,
                })
            }
            AutomationFrequency::Weekly => {
                for key in parts.keys() {
                    if key != "FREQ" && key != "BYDAY" && key != "BYHOUR" && key != "BYMINUTE" {
                        bail!(
                            "Unsupported RRULE field '{key}' for WEEKLY. Allowed: FREQ,BYDAY,BYHOUR,BYMINUTE"
                        );
                    }
                }
                let byday_raw = parts
                    .get("BYDAY")
                    .ok_or_else(|| anyhow::anyhow!("WEEKLY schedules require BYDAY"))?;
                let byday = parse_byday(&byday_raw.to_ascii_uppercase())?;
                if byday.is_empty() {
                    bail!("BYDAY cannot be empty for WEEKLY schedules");
                }
                let byhour = parts
                    .get("BYHOUR")
                    .ok_or_else(|| anyhow::anyhow!("WEEKLY schedules require BYHOUR"))?
                    .parse::<u32>()
                    .context("Failed to parse BYHOUR")?;
                let byminute = parts
                    .get("BYMINUTE")

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. For plain weekly schedules, drop INTERVAL: `FREQ=WEEKLY;BYDAY=MO,WE;BYHOUR=9;BYMINUTE=30`.
  2. For biweekly/fortnightly, use `FREQ=CRON;EXPR=30 9 * * 1` style only if weekly cadence matches, or schedule two rules on alternating weeks in your own driver.
  3. Check each key against FREQ,BYDAY,BYHOUR,BYMINUTE before submitting.

Example fix

// before
let s = AutomationSchedule::parse_rrule("FREQ=WEEKLY;INTERVAL=2;BYDAY=MO;BYHOUR=9;BYMINUTE=0")?; // bails on INTERVAL

// after
let s = AutomationSchedule::parse_rrule("FREQ=WEEKLY;BYDAY=MO;BYHOUR=9;BYMINUTE=0")?;
Defensive patterns

Strategy: validation

Validate before calling

const WEEKLY_ALLOWED: &[&str] = &["FREQ", "BYDAY", "BYHOUR", "BYMINUTE"];
for (k, _) in rrule.split(';').filter_map(|p| p.trim().split_once('=')) {
    if !WEEKLY_ALLOWED.contains(&k.trim().to_ascii_uppercase().as_str()) {
        anyhow::bail!("{} is not allowed for FREQ=WEEKLY (no INTERVAL)", k);
    }
}

Type guard

fn weekly_keys_allowed(rrule: &str) -> bool {
    rrule.split(';').filter_map(|p| p.trim().split_once('=')).all(|(k, _)| {
        ["FREQ", "BYDAY", "BYHOUR", "BYMINUTE"].contains(&k.trim().to_ascii_uppercase().as_str())
    })
}

Try / catch

match AutomationSchedule::parse_rrule(&rrule) {
    Ok(s) => s,
    Err(e) if e.to_string().contains("for WEEKLY") => { /* drop INTERVAL/UNTIL/COUNT or switch to CRON */ return Err(e) }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: parse_rrule("FREQ=WEEKLY;INTERVAL=2;BYDAY=MO;BYHOUR=9;BYMINUTE=0") (fortnightly via INTERVAL), or `FREQ=WEEKLY;COUNT=4;...`.

Common situations: Copying an RFC 5545 biweekly rule (`FREQ=WEEKLY;INTERVAL=2`) from a calendar system; trying to bound or offset a weekly schedule with standard RRULE parts.

Related errors


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