{"record":{"id":"7e3ab12928db3d6f","repo":"Hmbown/CodeWhale","slug":"rrule-must-include-freq","errorCode":null,"errorMessage":"RRULE must include FREQ","messagePattern":"RRULE must include FREQ","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/automation_manager.rs","lineNumber":310,"sourceCode":"            let Some((k, v)) = item.split_once('=') else {\n                bail!(\"Invalid RRULE segment '{item}'\");\n            };\n            parts.insert(k.trim().to_ascii_uppercase(), v.trim().to_string());\n        }\n\n        let freq = match parts\n            .get(\"FREQ\")\n            .map(|value| value.trim().to_ascii_uppercase())\n            .as_deref()\n        {\n            Some(\"ONCE\") => return parse_once_schedule(&parts),\n            Some(\"HOURLY\") => AutomationFrequency::Hourly,\n            Some(\"WEEKLY\") => AutomationFrequency::Weekly,\n            Some(\"CRON\") => return parse_cron_schedule(&parts),\n            Some(other) => {\n                bail!(\"Unsupported RRULE FREQ '{other}'. Supported: ONCE, HOURLY, WEEKLY, and CRON\")\n            }\n            None => bail!(\"RRULE must include FREQ\"),\n        };\n\n        match freq {\n            AutomationFrequency::Hourly => {\n                for key in parts.keys() {\n                    if key != \"FREQ\"\n                        && key != \"INTERVAL\"\n                        && key != \"BYDAY\"\n                        && key != \"BYHOUR\"\n                        && key != \"BYMINUTE\"\n                    {\n                        bail!(\n                            \"Unsupported RRULE field '{key}' for HOURLY. Allowed: FREQ,INTERVAL,BYDAY,BYHOUR,BYMINUTE\"\n                        );\n                    }\n                }\n                let interval_hours = parts\n                    .get(\"INTERVAL\")","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/automation_manager.rs#L292-L328","documentation":"After collecting KEY=VALUE segments, parse_rrule looks up the `FREQ` key (case-insensitive) and finds none. FREQ is the mandatory first decision (which schedule variant to build), so an RRULE without it cannot be interpreted at all. Empty segments were already skipped, so a string of only separators or whitespace also lands here.","triggerScenarios":"Calling parse_rrule with `\"BYDAY=MO;BYHOUR=9\"`, `\"\"`, `\";;;\"`, or a rule where FREQ is misspelled (`FREQ=` empty also yields no usable value once consumed elsewhere — the key must exist).","commonSituations":"Building RRULE strings programmatically and forgetting to append the FREQ part; user config with only the day/time fields; strings that got truncated before FREQ.","solutions":["Start every rule with a FREQ part: `FREQ=ONCE|HOURLY|WEEKLY|CRON;...`.","Validate presence before parsing: `rrule.split(';').any(|p| p.trim().to_ascii_uppercase().starts_with(\"FREQ=\"))`.","If the rule is user-supplied, reject it at the UI boundary with the supported-grammar hint."],"exampleFix":"// before\nlet sched = AutomationSchedule::parse_rrule(\"BYDAY=MO;BYHOUR=9\")?; // bails: RRULE must include FREQ\n\n// after\nlet sched = AutomationSchedule::parse_rrule(\"FREQ=WEEKLY;BYDAY=MO;BYHOUR=9;BYMINUTE=0\")?;","handlingStrategy":"validation","validationCode":"if !rrule\n    .split(';')\n    .any(|p| p.trim().to_ascii_uppercase().starts_with(\"FREQ=\"))\n{\n    anyhow::bail!(\"RRULE must start with FREQ=ONCE|HOURLY|WEEKLY|CRON\");\n}","typeGuard":"fn has_freq_part(rrule: &str) -> bool {\n    rrule.split(';').any(|p| p.trim().to_ascii_uppercase().starts_with(\"FREQ=\"))\n}","tryCatchPattern":"match AutomationSchedule::parse_rrule(&rrule) {\n    Ok(s) => s,\n    Err(e) if e.to_string().contains(\"must include FREQ\") => { /* prepend FREQ=... or re-prompt */ return Err(e) }\n    Err(e) => return Err(e),\n}","preventionTips":["Always construct RRULEs starting from a FREQ part chosen by enum, not string parts.","Require non-empty rules in config validation.","Unit-test your RRULE builder emits FREQ first."],"tags":["rrule","automation","schedule","missing-field"],"backgroundTag":"rrule-missing-frequency","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}