robfig/cron · error
failed to parse duration
Error message
failed to parse duration %s: %s
What it means
Thrown by parseDescriptor in parser.go when a descriptor starting with the '@every ' prefix has a suffix that time.ParseDuration cannot parse. The remainder after '@every ' must be a valid Go duration string such as '1h30m' or '15m'. The underlying ParseDuration error is embedded in the message.
Solutions
- Use a valid Go duration string: units are ns, us, ms, s, m, h (e.g. '@every 1h30m')
- Convert days/weeks yourself: '@every 24h' for daily, '@every 168h' for weekly
- Validate the duration from config with time.ParseDuration before embedding it in the descriptor
- If you need '1d' style syntax, write a wrapper that translates human durations to Go durations before calling Parse
Example fix
// before
cron.Parse("@every 1d") // 'd' is not a Go duration unit
// after
cron.Parse("@every 24h") Defensive patterns
Strategy: validation
Validate before calling
if strings.HasPrefix(spec, "@every ") {
if _, err := time.ParseDuration(strings.TrimPrefix(spec, "@every ")); err != nil {
return fmt.Errorf("invalid @every duration: %w", err)
}
} Try / catch
sched, err := cron.Parse(spec)
if err != nil {
return fmt.Errorf("invalid cron spec %q: %w", spec, err)
} Prevention
- Use only Go duration units: ns, us, ms, s, m, h — never 'd' or 'w'
- Test duration config values with time.ParseDuration at load time
- Convert human units (days, weeks) to hours before building the descriptor
When it happens
Trigger: Calling cron.Parse with '@every xyz', '@every 5', '@every 5min' (Go durations require 'm' for minutes, not 'min'... actually 'min' is invalid; must be 'm'), '@every 1d' (days are not a Go duration unit), or '@every' followed by an empty/whitespace suffix.
Common situations: Users writing human-style durations ('1d', '5min', '2w') that Go's duration syntax doesn't accept; config values with missing units ('@every 30'); localizing the string so the prefix matches but the number doesn't; Go version migrations are irrelevant here — the unit set is fixed by time.ParseDuration.
Understand the failure class
Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- unrecognized descriptor
- multiple optionals may not be configured
- empty spec string
- provided bad location
- parser does not accept descriptors
AI-assisted analysis of robfig/cron@bc59245fe1 (2026-09-07).
Data as JSON: /api/errors/8ebf92e553bce660.
Report an issue: GitHub.
Appendix: source
Thrown at parser.go:428
case "@hourly":
return &SpecSchedule{
Second: 1 << seconds.min,
Minute: 1 << minutes.min,
Hour: all(hours),
Dom: all(dom),
Month: all(months),
Dow: all(dow),
Location: loc,
}, nil
}
const every = "@every "
if strings.HasPrefix(descriptor, every) {
duration, err := time.ParseDuration(descriptor[len(every):])
if err != nil {
return nil, fmt.Errorf("failed to parse duration %s: %s", descriptor, err)
}
return Every(duration), nil
}
return nil, fmt.Errorf("unrecognized descriptor: %s", descriptor)
}
View on GitHub (pinned to bc59245fe1)