{"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":837,"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":819,"sourceCodeEnd":855,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/automation_manager.rs#L819-L855","documentation":"A step value in a cron field (the '/n' in '*​/n' or 'range/n') must be at least 1. A step of 0 is meaningless — it would repeat with no advancement — so the parser rejects it with the field name interpolated.","triggerScenarios":"Writing '*/0 * * * *', '0 0 1-10/0 * *', or computing the step from a variable that is 0 (e.g. interval_minutes=0).","commonSituations":"Config value of 0 intended to mean 'every' or unset; division-derived step rounding to 0; typo using /0 instead of *.","solutions":["Replace the 0 step with a real positive step, e.g. '*/5' for every 5 minutes","Use '*' alone to mean every unit instead of '*/0'","Validate the step variable is >= 1 at the call site before building the expression"],"exampleFix":"// before\nlet expr = format!(\"*/{} * * * *\", interval); // interval = 0\n// after\nassert!(interval >= 1);\nlet expr = if interval == 1 { \"*\".into() } else { format!(\"*/{} * * * *\", interval) };","handlingStrategy":"validation","validationCode":"assert!(step >= 1, \"cron step must be >= 1\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default a 0/unset interval to 1 or to a bare '*'","Validate config-sourced interval values before formatting into expressions","Never use */0 intending 'every' — that is just *"],"tags":["cron","validation","step"],"backgroundTag":"value-out-of-range","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"}