Hmbown/CodeWhale · error · anyhow::Error
CRON step must be >= 1
Error message
CRON {field_name} step must be >= 1 What it means
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.
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
Example fix
// before
let expr = format!("*/{} * * * *", interval); // interval = 0
// after
assert!(interval >= 1);
let expr = if interval == 1 { "*".into() } else { format!("*/{} * * * *", interval) }; Defensive patterns
Strategy: validation
Validate before calling
assert!(step >= 1, "cron step must be >= 1");
Prevention
- 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 *
When it happens
Trigger: Writing '*/0 * * * *', '0 0 1-10/0 * *', or computing the step from a variable that is 0 (e.g. interval_minutes=0).
Common situations: Config value of 0 intended to mean 'every' or unset; division-derived step rounding to 0; typo using /0 instead of *.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- CRON EXPR day-of-month/month combination can never occur
- CRON EXPR must have exactly 5 fields: minute hour…
- CRON field contains an empty list item
- CRON field must not be empty
- CRON range start must be <= end
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e47b883b37902759.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/automation_manager.rs:837
fn parse(raw: &str, min: u32, max: u32, names: CronNameMap, field_name: &str) -> Result<Self> {
let trimmed = raw.trim();
if trimmed.is_empty() {
bail!("CRON {field_name} field must not be empty");
}
let mut values = Vec::new();
let is_wildcard = trimmed == "*";
for part in trimmed.split(',') {
let part = part.trim();
if part.is_empty() {
bail!("CRON {field_name} field contains an empty list item");
}
let (base, step) = if let Some((base, step)) = part.split_once('/') {
let step = step
.trim()
.parse::<u32>()
.with_context(|| format!("Failed to parse CRON {field_name} step"))?;
if step == 0 {
bail!("CRON {field_name} step must be >= 1");
}
(base.trim(), step)
} else {
(part, 1)
};
let range = if base == "*" {
(min, max)
} else if let Some((start, end)) = base.split_once('-') {
let start = parse_cron_atom(start.trim(), min, max, names, field_name)?;
let end = parse_cron_atom(end.trim(), min, max, names, field_name)?;
if start > end {
bail!("CRON {field_name} range start must be <= end");
}
(start, end)
} else {
let start = parse_cron_atom(base, min, max, names, field_name)?;
if part.contains('/') {View on GitHub (pinned to 73e0f67d83)