robfig/cron · error

step of range should be a positive number

Error message

step of range should be a positive number: %s

What it means

Thrown by getRange in parser.go when a range step parses to 0, e.g. '*/0' or '1-10/0'. A zero step would divide the range infinitely, so the parser rejects it and requires a strictly positive step. Note that strconv.Atoi also accepts '+0'/'00' forms that reach this check rather than the int-parse error.

Solutions

  1. Ensure the step value is >= 1 before formatting it into the spec string
  2. Validate interval config values at load time and reject/substitute a sensible default when 0
  3. If you meant 'never run', don't register the schedule at all instead of using a 0 step
  4. Wrap cron.Parse at startup so invalid specs surface immediately

Example fix

// before
if interval == 0 { spec = "*/" + fmt.Sprint(interval) /* -> */0 */ }
// after
if interval <= 0 { return errors.New("interval must be positive") }
spec = "*/" + fmt.Sprint(interval)
Defensive patterns

Strategy: validation

Validate before calling

func validStep(step int) bool { return step >= 1 }
// reject interval config values of 0 before formatting "*/%d"

Try / catch

sched, err := cron.Parse(spec)
if err != nil {
	return fmt.Errorf("invalid cron spec %q: %w", spec, err)
}

Prevention

When it happens

Trigger: Calling cron.Parse with specs like '0 */0 * * * *', '0 0 0 1-31/0 * *', or a template where the step value came from a variable that was 0 (e.g. an unset interval config defaulting to 0) and was interpolated as 'N/0'.

Common situations: Config-driven interval values where an unset/zero default gets interpolated into the spec; users typing '*/0' expecting 'never run'; JSON/YAML config with numeric 0 interval lacking zero-validation before string building.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of robfig/cron@bc59245fe1 (2026-09-07). Data as JSON: /api/errors/672ec47125fe8dae. Report an issue: GitHub.

Appendix: source

Thrown at parser.go:314

		}
		if step > 1 {
			extra = 0
		}
	default:
		return 0, fmt.Errorf("too many slashes: %s", expr)
	}

	if start < r.min {
		return 0, fmt.Errorf("beginning of range (%d) below minimum (%d): %s", start, r.min, expr)
	}
	if end > r.max {
		return 0, fmt.Errorf("end of range (%d) above maximum (%d): %s", end, r.max, expr)
	}
	if start > end {
		return 0, fmt.Errorf("beginning of range (%d) beyond end of range (%d): %s", start, end, expr)
	}
	if step == 0 {
		return 0, fmt.Errorf("step of range should be a positive number: %s", expr)
	}

	return getBits(start, end, step) | extra, nil
}

// parseIntOrName returns the (possibly-named) integer contained in expr.
func parseIntOrName(expr string, names map[string]uint) (uint, error) {
	if names != nil {
		if namedInt, ok := names[strings.ToLower(expr)]; ok {
			return namedInt, nil
		}
	}
	return mustParseInt(expr)
}

// mustParseInt parses the given expression as an int or returns an error.
func mustParseInt(expr string) (uint, error) {
	num, err := strconv.Atoi(expr)

View on GitHub (pinned to bc59245fe1)