hashicorp/nomad · error
Invalid cron spec %q: %v
Error message
Invalid cron spec %q: %v
What it means
PeriodicConfig.Validate parses the job's single cron Spec with the cronexpr library when SpecType is PeriodicSpecCron. If cronexpr.Parse returns an error for the expression, the error is wrapped and appended to the validation multierror. The expression never evaluated, so the job cannot be scheduled periodically.
Source
Thrown at nomad/structs/structs.go:5936
_ = multierror.Append(&mErr, fmt.Errorf("Only cron or crons may be used"))
}
if p.Spec == "" && len(p.Specs) == 0 {
_ = multierror.Append(&mErr, fmt.Errorf("Must specify a spec"))
}
// Check if we got a valid time zone
if p.TimeZone != "" {
if _, err := time.LoadLocation(p.TimeZone); err != nil {
_ = multierror.Append(&mErr, fmt.Errorf("Invalid time zone %q: %v", p.TimeZone, err))
}
}
switch p.SpecType {
case PeriodicSpecCron:
// Validate the cron spec
if p.Spec != "" {
if _, err := cronexpr.Parse(p.Spec); err != nil {
_ = multierror.Append(&mErr, fmt.Errorf("Invalid cron spec %q: %v", p.Spec, err))
}
}
// Validate the cron specs
for _, spec := range p.Specs {
if _, err := cronexpr.Parse(spec); err != nil {
_ = multierror.Append(&mErr, fmt.Errorf("Invalid cron spec %q: %v", spec, err))
}
}
case PeriodicSpecTest:
// No-op
default:
_ = multierror.Append(&mErr, fmt.Errorf("Unknown periodic specification type %q", p.SpecType))
}
return mErr.ErrorOrNil()
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Fix the cron expression to a valid cronexpr-parseable spec (5 fields: min hour dom mon dow), e.g. "*/15 * * * *".
- Test the expression locally with cronexpr.Parse or an online cron validator before submitting.
- If multiple schedules are needed, move them into the Specs list, each individually valid.
- Verify spec_type is actually "cron" so the expression is parsed with the right grammar.
Example fix
// before
periodic {
spec = "0 9 * * MON-FRI extra"
}
// after
periodic {
spec = "0 9 * * MON-FRI"
} Defensive patterns
Strategy: validation
Validate before calling
import "github.com/hashicorp/cronexpr"
func validCron(spec string) bool {
_, err := cronexpr.Parse(spec)
return err == nil
}
// call before submitting: if !validCron(job.Periodic.Spec) { ... } Try / catch
t, err := structs.CronParseNext(time.Now(), spec)
if err != nil {
log.Printf("bad cron spec %q: %v", spec, err)
// fall back or fix input
} Prevention
- Always run `nomad job validate` before `nomad job run`.
- Use a 5-field cron expression; avoid Quartz/@descriptor dialects.
- Test expressions with cronexpr.Parse locally first.
- Keep specs in a linted config source, not ad-hoc edits.
When it happens
Trigger: Submitting a periodic job with `periodic { spec = "...", spec_type = "cron" }` where the cron expression fails cronexpr.Parse — e.g. "* * *" (too few fields), "*/abc * * * *", or stray words.
Common situations: Copy-pasting cron expressions from systems with different dialects (e.g. 5-field vs Quartz 6-field); typos in step values; using @daily-style descriptors not supported by cronexpr.
Related errors
- Disconnect cannot be configured with both lost_after and sto
- lost_after cannot be a negative duration
- stop_after cannot be a negative duration
- Missing job ID
- Job ID contains a space
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/beda8d88409218da.
Report an issue: GitHub.