hashicorp/nomad · error

failed parsing cron expression: %s: %v

Error message

failed parsing cron expression: %s: %v

What it means

In CronParseNext, if cronexpr.Parse itself fails to parse the spec, the parse error is wrapped into this message containing the spec and the underlying error. Unlike the panic-recovery path, this fires before any Next() evaluation, directly from cronexpr.Parse.

Source

Thrown at nomad/structs/structs.go:5976

	if err != nil {
		p.location = time.UTC
	}

	p.location = l
}

// CronParseNext is a helper that parses the next time for the given expression
// but captures any panic that may occur in the underlying library.
func CronParseNext(fromTime time.Time, spec string) (t time.Time, err error) {
	defer func() {
		if recover() != nil {
			t = time.Time{}
			err = fmt.Errorf("failed parsing cron expression: %q", spec)
		}
	}()
	exp, err := cronexpr.Parse(spec)
	if err != nil {
		return time.Time{}, fmt.Errorf("failed parsing cron expression: %s: %v", spec, err)
	}
	return exp.Next(fromTime), nil
}

// Next returns the closest time instant matching the spec that is after the
// passed time. If no matching instance exists, the zero value of time.Time is
// returned. The `time.Location` of the returned value matches that of the
// passed time.
func (p *PeriodicConfig) Next(fromTime time.Time) (time.Time, error) {
	switch p.SpecType {
	case PeriodicSpecCron:
		// Single spec parsing
		if p.Spec != "" {
			return CronParseNext(fromTime, p.Spec)
		}

		// multiple specs parsing
		var nextTime time.Time

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Correct the spec string reported in the error to a valid 5-field cron expression.
  2. Run cronexpr.Parse on the spec before calling CronParseNext to fail fast.
  3. Ensure the job goes through job validation (which catches bad specs) before being registered.
  4. Trim whitespace/hidden characters from programmatically built spec strings.

Example fix

// before
t, err := structs.CronParseNext(now, "* * *")
// after
t, err := structs.CronParseNext(now, "* * * * *")
Defensive patterns

Strategy: validation

Validate before calling

func safeCronParseNext(from time.Time, spec string) (time.Time, error) {
    if _, err := cronexpr.Parse(spec); err != nil {
        return time.Time{}, err // fail before CronParseNext
    }
    return structs.CronParseNext(from, spec)
}

Try / catch

t, err := structs.CronParseNext(fromTime, spec)
if err != nil {
    return time.Time{}, fmt.Errorf("skipping eval: %w", err)
}

Prevention

When it happens

Trigger: Calling structs.CronParseNext (directly or via PeriodicConfig.Next) with a spec string that cronexpr.Parse rejects — wrong field count, invalid tokens, malformed steps.

Common situations: Runtime dispatch of a periodic job whose spec bypassed validation (e.g. stored pre-validation or constructed programmatically); config loaded from an API with hand-built specs.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/609521aa9fb0c859. Report an issue: GitHub.