{"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":655,"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":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/automation_manager.rs#L637-L673","documentation":"ParsedCronExpr::parse splits EXPR on whitespace and requires exactly five fields (minute hour day-of-month month day-of-week). Six-field expressions with a leading seconds field or trailing year, and truncated four-field expressions, are rejected before any field is parsed.","triggerScenarios":"EXPR='0 */17 * * * *' (Quartz-style seconds prefix); EXPR='*/17 * * *' (four fields); shell quoting that drops a field when passing the automation tool input.","commonSituations":"Porting crontabs from Quartz/cronie variants allowing 6-7 fields; quoting mistakes; hand-truncated examples from docs.","solutions":["Drop the leading seconds field: */17 * * * *","Drop a trailing year field if present","Count whitespace-separated tokens before submitting (must be 5)","Pre-validate with AutomationSchedule::parse_rrule(\"FREQ=CRON;EXPR=...\")"],"exampleFix":"// before\nrrule = \"FREQ=CRON;EXPR=0 */17 * * * *\"\n\n// after\nrrule = \"FREQ=CRON;EXPR=*/17 * * * *\"","handlingStrategy":"validation","validationCode":"fn is_five_field_cron(expr: &str) -> bool {\n    expr.split_whitespace().count() == 5\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Standardize on Vixie-cron 5-field syntax; drop seconds/year fields when porting","Lint EXPR with a 5-token count at input time","Watch quoting: EXPR is one value inside the rrule, but spaces inside it are fine","Dry-run the whole rrule through parse_rrule before saving"],"tags":["rust","cron","validation","syntax"],"backgroundTag":"cron-field-count-mismatch","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}