{"record":{"id":"fa3bd608ba343a4d","repo":"Hmbown/CodeWhale","slug":"unsupported-rrule-field-key-for-cron-allowed","errorCode":null,"errorMessage":"Unsupported RRULE field '{key}' for CRON. Allowed: FREQ,EXPR","messagePattern":"Unsupported RRULE field '(.+?)' for CRON\\. Allowed: FREQ,EXPR","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/automation_manager.rs","lineNumber":615,"sourceCode":"}\n\nfn parse_once_schedule(parts: &BTreeMap<String, String>) -> Result<AutomationSchedule> {\n    for key in parts.keys() {\n        if key != \"FREQ\" && key != \"AT\" {\n            bail!(\"Unsupported RRULE field '{key}' for ONCE. Allowed: FREQ,AT\");\n        }\n    }\n    let raw_at = parts\n        .get(\"AT\")\n        .ok_or_else(|| anyhow::anyhow!(\"ONCE schedules require AT\"))?;\n    let at = parse_once_at(raw_at)?;\n    Ok(AutomationSchedule::Once { at })\n}\n\nfn parse_cron_schedule(parts: &BTreeMap<String, String>) -> Result<AutomationSchedule> {\n    for key in parts.keys() {\n        if key != \"FREQ\" && key != \"EXPR\" {\n            bail!(\"Unsupported RRULE field '{key}' for CRON. Allowed: FREQ,EXPR\");\n        }\n    }\n    let expr = parts\n        .get(\"EXPR\")\n        .map(|value| value.trim().to_string())\n        .filter(|value| !value.is_empty())\n        .ok_or_else(|| anyhow::anyhow!(\"CRON schedules require EXPR\"))?;\n    ParsedCronExpr::parse(&expr)?;\n    Ok(AutomationSchedule::Cron { expr })\n}\n\nfn parse_once_at(raw: &str) -> Result<DateTime<Utc>> {\n    let trimmed = raw.trim();\n    if let Ok(at) = DateTime::parse_from_rfc3339(trimmed) {\n        return Ok(at.with_timezone(&Utc));\n    }\n    for format in [\"%Y-%m-%dT%H:%M:%S\", \"%Y-%m-%dT%H:%M\"] {\n        if let Ok(naive) = NaiveDateTime::parse_from_str(trimmed, format) {","sourceCodeStart":597,"sourceCodeEnd":633,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/automation_manager.rs#L597-L633","documentation":"parse_cron_schedule rejects any RRULE key other than FREQ and EXPR for FREQ=CRON. Keys are uppercased before the check, so it is case-insensitive; a missing or empty EXPR produces 'CRON schedules require EXPR' instead.","triggerScenarios":"AutomationSchedule::parse_rrule(\"FREQ=CRON;EXPR=*/17 * * * *;INTERVAL=2\"), or any FREQ=CRON rule carrying BYDAY, BYHOUR, AT, or similar extra keys.","commonSituations":"Mixing RRULE-style fields into cron rules; converting an HOURLY rule to CRON and forgetting to remove INTERVAL/BYDAY.","solutions":["Keep only FREQ and EXPR: FREQ=CRON;EXPR=*/17 * * * *","Encode cadence entirely inside the 5-field EXPR","Move one-shot times to FREQ=ONCE;AT=...","Dry-run parse_rrule before persisting"],"exampleFix":"// before\nrrule = \"FREQ=CRON;EXPR=*/17 * * * *;INTERVAL=2\"\n\n// after\nrrule = \"FREQ=CRON;EXPR=*/17 * * * *\"","handlingStrategy":"validation","validationCode":"fn valid_cron_rrule(rrule: &str) -> bool {\n    rrule.split(';')\n        .filter_map(|s| s.split_once('=').map(|(k, _)| k.trim().to_ascii_uppercase()))\n        .all(|k| k == \"FREQ\" || k == \"EXPR\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Encode all cadence inside the 5-field EXPR","Strip RRULE-style extras when converting HOURLY/WEEKLY rules to CRON","Validate the assembled FREQ=CRON;EXPR=... string with parse_rrule","Keep one-shot times in FREQ=ONCE;AT=... instead of EXPR"],"tags":["rust","rrule","cron","validation"],"backgroundTag":"rrule-unsupported-field","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}