{"record":{"id":"d6adb21a09ca30cb","repo":"Hmbown/CodeWhale","slug":"byday-cannot-be-empty-for-weekly-schedules","errorCode":null,"errorMessage":"BYDAY cannot be empty for WEEKLY schedules","messagePattern":"BYDAY cannot be empty for WEEKLY schedules","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/automation_manager.rs","lineNumber":376,"sourceCode":"                    byday,\n                    anchor_hour,\n                    anchor_minute,\n                })\n            }\n            AutomationFrequency::Weekly => {\n                for key in parts.keys() {\n                    if key != \"FREQ\" && key != \"BYDAY\" && key != \"BYHOUR\" && key != \"BYMINUTE\" {\n                        bail!(\n                            \"Unsupported RRULE field '{key}' for WEEKLY. Allowed: FREQ,BYDAY,BYHOUR,BYMINUTE\"\n                        );\n                    }\n                }\n                let byday_raw = parts\n                    .get(\"BYDAY\")\n                    .ok_or_else(|| anyhow::anyhow!(\"WEEKLY schedules require BYDAY\"))?;\n                let byday = parse_byday(&byday_raw.to_ascii_uppercase())?;\n                if byday.is_empty() {\n                    bail!(\"BYDAY cannot be empty for WEEKLY schedules\");\n                }\n                let byhour = parts\n                    .get(\"BYHOUR\")\n                    .ok_or_else(|| anyhow::anyhow!(\"WEEKLY schedules require BYHOUR\"))?\n                    .parse::<u32>()\n                    .context(\"Failed to parse BYHOUR\")?;\n                let byminute = parts\n                    .get(\"BYMINUTE\")\n                    .ok_or_else(|| anyhow::anyhow!(\"WEEKLY schedules require BYMINUTE\"))?\n                    .parse::<u32>()\n                    .context(\"Failed to parse BYMINUTE\")?;\n\n                if byhour > 23 {\n                    bail!(\"BYHOUR must be between 0 and 23\");\n                }\n                if byminute > 59 {\n                    bail!(\"BYMINUTE must be between 0 and 59\");\n                }","sourceCodeStart":358,"sourceCodeEnd":394,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/automation_manager.rs#L358-L394","documentation":"A defensive guard in the WEEKLY branch: BYDAY is present and parse_byday returned Ok with an empty weekday vector, meaning no day of week would ever match. In practice parse_byday rejects each empty/unknown token with 'Invalid BYDAY value', so reaching this requires a BYDAY value that tokenizes to zero entries — it protects next-run computation from looping forever on an unsatisfiable schedule.","triggerScenarios":"parse_rrule(\"FREQ=WEEKLY;BYDAY=;BYHOUR=9;BYMINUTE=0\") — a BYDAY key with an empty value generally trips 'Invalid BYDAY value' first; this bail catches any path where the day set ends up empty (future grammar changes, whitespace-only tokens).","commonSituations":"Config generators emitting `BYDAY=` when no days are selected; form submissions with an empty multi-select for weekdays.","solutions":["Provide at least one valid day code (MO,TU,WE,TH,FR,SA,SU): `FREQ=WEEKLY;BYDAY=MO,SU;...`.","If no days are selected, either block submission in the UI or convert to a CRON/HOURLY rule instead.","Lint WEEKLY rules for a non-empty, comma-separated, all-caps day list before parsing."],"exampleFix":"// before\nlet rrule = format!(\"FREQ=WEEKLY;BYDAY={days};BYHOUR=9;BYMINUTE=0\"); // days == \"\" -> error path\n\n// after\nif days.is_empty() {\n    anyhow::bail!(\"select at least one weekday\");\n}\nlet rrule = format!(\"FREQ=WEEKLY;BYDAY={days};BYHOUR=9;BYMINUTE=0\");","handlingStrategy":"validation","validationCode":"if !rrule\n    .split(';')\n    .any(|p| p.trim().to_ascii_uppercase().starts_with(\"BYDAY=\"))\n    || rrule.split(';').find(|p| p.trim().to_ascii_uppercase().starts_with(\"BYDAY=\")).is_some_and(|p| p[6..].trim().is_empty())\n{\n    anyhow::bail!(\"WEEKLY schedules need at least one day: BYDAY=MO,TU,...\");\n}","typeGuard":"fn weekly_has_days(rrule: &str) -> bool {\n    rrule.split(';').find_map(|p| p.trim().strip_prefix(\"BYDAY=\").or_else(|| p.trim().strip_prefix(\"BYDAY=\")))\n        .is_some_and(|v| v.split(',').any(|t| !t.trim().is_empty()))\n}","tryCatchPattern":"match AutomationSchedule::parse_rrule(&rrule) {\n    Ok(s) => s,\n    Err(e) if e.to_string().contains(\"BYDAY cannot be empty\") => { /* require a weekday selection */ return Err(e) }\n    Err(e) => return Err(e),\n}","preventionTips":["Require at least one selected weekday in schedule forms before submit.","Skip emitting the BYDAY key when no days are chosen rather than writing an empty value.","Validate day tokens against MO,TU,WE,TH,FR,SA,SU."],"tags":["rrule","automation","schedule","byday"],"backgroundTag":"rrule-empty-byday","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}