{"record":{"id":"7a6f9399749d4368","repo":"Hmbown/CodeWhale","slug":"cron-expr-must-have-exactly-5-fields-minute-hour","errorCode":null,"errorMessage":"CRON EXPR must have exactly 5 fields: minute hour day-of-month month day-of-week","messagePattern":"CRON EXPR must have exactly 5 fields: minute hour day-of-month month day-of-week","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/automation_manager.rs","lineNumber":754,"sourceCode":"        }\n    }\n    bail!(\"Failed to parse ONCE AT '{trimmed}'. Use local YYYY-MM-DDTHH:MM[:SS] or RFC3339\")\n}\n\n#[derive(Debug, Clone)]\nstruct ParsedCronExpr {\n    minute: CronField,\n    hour: CronField,\n    day_of_month: CronField,\n    month: CronField,\n    day_of_week: CronField,\n}\n\nimpl ParsedCronExpr {\n    fn parse(expr: &str) -> Result<Self> {\n        let fields: Vec<&str> = expr.split_whitespace().collect();\n        if fields.len() != 5 {\n            bail!(\n                \"CRON EXPR must have exactly 5 fields: minute hour day-of-month month day-of-week\"\n            );\n        }\n        let parsed = Self {\n            minute: CronField::parse(fields[0], 0, 59, CronNameMap::none(), \"minute\")?,\n            hour: CronField::parse(fields[1], 0, 23, CronNameMap::none(), \"hour\")?,\n            day_of_month: CronField::parse(fields[2], 1, 31, CronNameMap::none(), \"day-of-month\")?,\n            month: CronField::parse(fields[3], 1, 12, CronNameMap::month(), \"month\")?,\n            day_of_week: CronField::parse(fields[4], 0, 7, CronNameMap::weekday(), \"day-of-week\")?\n                .normalized_day_of_week(),\n        };\n        parsed.validate_date_space()?;\n        Ok(parsed)\n    }\n\n    fn matches(&self, candidate: NaiveDateTime) -> bool {\n        if !self.minute.contains(candidate.minute())\n            || !self.hour.contains(candidate.hour())","sourceCodeStart":736,"sourceCodeEnd":772,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/automation_manager.rs#L736-L772","documentation":"ParsedCronExpr::parse splits the EXPR on whitespace and requires exactly five fields — minute, hour, day-of-month, month, day-of-week — before validating each field. Any other count bails with this message describing the expected layout. It fires both when saving a CRON schedule and when next_after_in_timezone re-parses the stored expression.","triggerScenarios":"AutomationSchedule::parse with FREQ=CRON;EXPR=<expr> where split_whitespace yields != 5 fields: 6-field cron with seconds, 3-field shorthand, or an expression with collapsed/extra whitespace not intended as fields.","commonSituations":"Users pasting 6-field (with seconds) cron from systemd or Quartz; omitting a field (e.g. '0 9 *' meaning daily 9am); extra spaces normally fine but a missing field is not.","solutions":["Provide exactly five whitespace-separated fields: 'minute hour day-of-month month day-of-week' (e.g. '0 9 * * *').","Drop the seconds field from 6-field cron expressions (this parser has minute granularity only).","Use '*' for fields you don't want to constrain instead of omitting them."],"exampleFix":"// before\nFREQ=CRON;EXPR=\"0 9 * * * *\"\n// after\nFREQ=CRON;EXPR=\"0 9 * * *\"","handlingStrategy":"validation","validationCode":"fn valid_cron_expr(expr: &str) -> bool {\n    expr.split_whitespace().count() == 5\n}","typeGuard":null,"tryCatchPattern":"match AutomationSchedule::parse(rrule) {\n    Ok(s) => save(s),\n    Err(e) if e.to_string().contains(\"exactly 5 fields\") => show_cron_format_help(&e),\n    Err(e) => report(e),\n}","preventionTips":["Reject 6-field (seconds) cron at input time with a clear message.","Provide field-by-field inputs (minute, hour, dom, month, dow) and join them with spaces.","Document that this parser supports standard 5-field cron only, minute granularity."],"tags":["validation","cron","parsing"],"backgroundTag":"invalid-argument-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}