{"record":{"id":"e47b883b37902759","repo":"Hmbown/CodeWhale","slug":"cron-field-name-step-must-be-1","errorCode":null,"errorMessage":"CRON {field_name} step must be >= 1","messagePattern":"CRON (.+?) step must be >= 1","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/automation_manager.rs","lineNumber":738,"sourceCode":"    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 {\n                (part, 1)\n            };\n\n            let range = if base == \"*\" {\n                (min, max)\n            } else if let Some((start, end)) = base.split_once('-') {\n                let start = parse_cron_atom(start.trim(), min, max, names, field_name)?;\n                let end = parse_cron_atom(end.trim(), min, max, names, field_name)?;\n                if start > end {\n                    bail!(\"CRON {field_name} range start must be <= end\");\n                }\n                (start, end)\n            } else {\n                let start = parse_cron_atom(base, min, max, names, field_name)?;\n                if part.contains('/') {","sourceCodeStart":720,"sourceCodeEnd":756,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/automation_manager.rs#L720-L756","documentation":"A step value of 0 in a cron field ('base/step') is rejected; steps must parse as u32 and be >= 1. '*/0', '5-10/0', and name-based bases with '/0' all fail here. A non-numeric step produces the sibling 'Failed to parse CRON {field} step' error instead.","triggerScenarios":"EXPR='*/0 * * * *' (every zero minutes); EXPR='0 0 */0 * *'; templated steps where a computed divisor evaluated to 0.","commonSituations":"Templated cron generators computing a step from user-supplied numbers; placeholder '*/N' strings with N never substituted.","solutions":["Use a step >= 1: */5, */15, 2-30/10","Guard computed steps against 0 before formatting the EXPR","For 'as often as possible' use plain '*' (implicit step 1)","Dry-run parse_rrule on the final expression"],"exampleFix":"// before\nrrule = \"FREQ=CRON;EXPR=*/0 * * * *\"\n\n// after\nrrule = \"FREQ=CRON;EXPR=*/5 * * * *\"","handlingStrategy":"validation","validationCode":"fn cron_steps_positive(expr: &str) -> bool {\n    expr.split_whitespace().all(|field| {\n        field.split(',').all(|item| match item.split_once('/') {\n            Some((_, step)) => step.trim().parse::<u32>().is_ok_and(|s| s >= 1),\n            None => true,\n        })\n    })\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp generated steps to at least 1","Treat step 0 as invalid at the UI layer before submission","Remember a missing step defaults to 1","Dry-run the EXPR via parse_rrule"],"tags":["rust","cron","step","validation"],"backgroundTag":"cron-invalid-step-value","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}