robfig/cron · error
failed to parse int from
Error message
failed to parse int from %s: %s
What it means
Thrown by mustParseInt in parser.go when a numeric token in a cron field cannot be parsed by strconv.Atoi. Any field component expected to be a plain non-negative integer (or a name like 'mon' when names are defined) that contains non-digit characters or overflows int reaches this error. The original strconv error is embedded in the message.
Solutions
- Inspect the token shown after 'failed to parse int from' and correct/remove the non-numeric characters
- Trim whitespace/BOM from spec strings read from files or env before parsing
- Use the NewParser with built-in name support or write the numeric equivalent instead of month/day names if the parser has no names map
- Validate the spec at application startup rather than lazily so bad config fails fast
Example fix
// before
cron.Parse("0 0 {{.hour}} * * *") // placeholder not substituted
// after
spec := fmt.Sprintf("0 0 %d * * *", hour) // hour is a validated int Defensive patterns
Strategy: validation
Validate before calling
func isNumericToken(s string) bool {
if s == "" { return false }
for _, r := range s { if r < '0' || r > '9' { return false } }
return true
} Try / catch
sched, err := cron.Parse(spec)
if err != nil {
return fmt.Errorf("invalid cron spec %q: %w", spec, err)
} Prevention
- Trim whitespace and BOM from specs read from files/env
- Verify template placeholders are substituted before parsing
- Use numeric values instead of month/day names unless the parser registers names
When it happens
Trigger: Calling cron.Parse with malformed numeric tokens: 'abc' in a numeric-only field like hours ('0 * abc * * *'), '01.5', '1e3', '99999999999999999999' (overflow), a stray character like '1O' (letter O instead of zero), or a week name like 'mon' in a field with no names map.
Common situations: Typos in hand-written crontab strings; locale characters or invisible whitespace (BOM, non-breaking spaces) inside the spec; template substitution leaving literal placeholders like '{{.minute}}'; copying Quartz names ('JAN', 'SUN') into fields of the default parser where names aren't registered.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- multiple optionals may not be configured
- parser does not accept descriptors
- expected exactly fields, found
- expected to fields, found
- unknown optional field
AI-assisted analysis of robfig/cron@bc59245fe1 (2026-09-07).
Data as JSON: /api/errors/cdd36505d672ac27.
Report an issue: GitHub.
Appendix: source
Thrown at parser.go:334
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)
if err != nil {
return 0, fmt.Errorf("failed to parse int from %s: %s", expr, err)
}
if num < 0 {
return 0, fmt.Errorf("negative number (%d) not allowed: %s", num, expr)
}
return uint(num), nil
}
// getBits sets all bits in the range [min, max], modulo the given step size.
func getBits(min, max, step uint) uint64 {
var bits uint64
// If step is 1, use shifts.
if step == 1 {
return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
}
// Else, use a simple loop.View on GitHub (pinned to bc59245fe1)