{"record":{"id":"c9fff1ce3361f106","repo":"Hmbown/CodeWhale","slug":"cron-field-name-field-must-not-be-empty","errorCode":null,"errorMessage":"CRON {field_name} field must not be empty","messagePattern":"CRON (.+?) field must not be empty","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/automation_manager.rs","lineNumber":822,"sourceCode":"        if valid {\n            Ok(())\n        } else {\n            bail!(\"CRON EXPR day-of-month/month combination can never occur\")\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\nstruct CronField {\n    values: Vec<u32>,\n    is_wildcard: bool,\n}\n\nimpl CronField {\n    fn parse(raw: &str, min: u32, max: u32, names: CronNameMap, field_name: &str) -> Result<Self> {\n        let trimmed = raw.trim();\n        if trimmed.is_empty() {\n            bail!(\"CRON {field_name} field must not be empty\");\n        }\n        let mut values = Vec::new();\n        let is_wildcard = trimmed == \"*\";\n        for part in trimmed.split(',') {\n            let part = part.trim();\n            if part.is_empty() {\n                bail!(\"CRON {field_name} field contains an empty list item\");\n            }\n            let (base, step) = if let Some((base, step)) = part.split_once('/') {\n                let step = step\n                    .trim()\n                    .parse::<u32>()\n                    .with_context(|| format!(\"Failed to parse CRON {field_name} step\"))?;\n                if step == 0 {\n                    bail!(\"CRON {field_name} step must be >= 1\");\n                }\n                (base.trim(), step)\n            } else {","sourceCodeStart":804,"sourceCodeEnd":840,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/automation_manager.rs#L804-L840","documentation":"CronField::parse rejects a field whose trimmed text is empty. Every cron expression must supply a non-empty value for each of the minute/hour/day/month/weekday fields, and an empty field would produce a field with no values. The {field_name} placeholder is interpolated with the actual field name.","triggerScenarios":"Parsing a cron expression with an empty component, e.g. \"30 * * * \" (trailing empty weekday field), \"  9 * * * *\" is fine but \"\" for any field after splitting, or programmatic construction like format!(\"{} * * * *\", empty_var).","commonSituations":"Template/variable interpolation where a config value is unset; copy-paste losing the last field; splitting an expression on whitespace and rejoining incorrectly.","solutions":["Provide an explicit value for the empty field (use * to mean 'every')","Check the source of the cron string — an env var or config key feeding the field is unset or empty","Trim and re-validate the whole expression before passing it in"],"exampleFix":"// before\nlet expr = format!(\"{} * * * *\", maybe_empty); // empty minute field\n// after\nlet minute = if maybe_empty.is_empty() { \"*\" } else { &maybe_empty };\nlet expr = format!(\"{} * * * *\", minute);","handlingStrategy":"validation","validationCode":"fn valid_cron(expr: &str) -> bool {\n    let fields: Vec<&str> = expr.split_whitespace().collect();\n    fields.len() == 5 && fields.iter().all(|f| !f.trim().is_empty())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always emit all 5 fields, using * for defaults","Guard interpolated values with an emptiness check before building the expression","Trim the assembled expression and re-check field count"],"tags":["cron","validation","empty-input"],"backgroundTag":"empty-required-field","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"}