hashicorp/nomad · error

failed adding job %s: %v

Error message

failed adding job %s: %v

What it means

PeriodicDispatcher.Add registers (or updates) a tracked periodic job and computes its next launch time via job.Periodic.Next(). If computing the next launch time fails, the job ID and the underlying error are wrapped and returned. Called by applyUpsertJob (state apply path) and restorePeriodicDispatcher (server startup).

Source

Thrown at nomad/periodic.go:219

	tuple := structs.NamespacedID{
		ID:        job.ID,
		Namespace: job.Namespace,
	}
	_, tracked := p.tracked[tuple]
	if disabled {
		if tracked {
			p.removeLocked(tuple)
		}

		// If the job is disabled and we aren't tracking it, do nothing.
		return nil
	}

	// Add or update the job.
	p.tracked[tuple] = job
	next, err := job.Periodic.Next(time.Now().In(job.Periodic.GetLocation()))
	if err != nil {
		return fmt.Errorf("failed adding job %s: %v", job.NamespacedID(), err)
	}
	if tracked {
		if err := p.heap.Update(job, next); err != nil {
			return fmt.Errorf("failed to update job %q (%s) launch time: %v", job.ID, job.Namespace, err)
		}
		p.logger.Debug("updated periodic job", "job", job.NamespacedID())
	} else {
		if err := p.heap.Push(job, next); err != nil {
			return fmt.Errorf("failed to add job %v: %v", job.ID, err)
		}
		p.logger.Debug("registered periodic job", "job", job.NamespacedID())
	}

	// Signal an update.
	select {
	case p.updateCh <- struct{}{}:
	default:
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Fix the job's Periodic.Spec / cron expression to a valid 5-field cron (or supported @-macro)
  2. Verify the job's TimeZone resolves on the server (IANA zone present in /usr/share/zoneinfo)
  3. Re-submit the corrected job with nomad job run; check restorePeriodicDispatcher logs for the offending job ID
  4. If caused by version incompatibility, migrate the job spec to the current format

Example fix

// before
periodic {
  cron = "* * * *"
}
// after
periodic {
  cron = "*/5 * * * *"
}
Defensive patterns

Strategy: validation

Validate before calling

// validate cron and tz before job submit
cronRe := regexp.MustCompile(`(@(annually|yearly|monthly|weekly|daily|hourly|reboot))|((((\d+,)+\d+|\d+(\/\d+)?|\*|\?|JAN|FEB|...)\s+){4,5})`)
if !cronRe.MatchString(job.Periodic.Spec) { return errors.New("invalid cron spec") }
if _, err := time.LoadLocation(job.TimeZone); err != nil { return errors.New("invalid timezone") }

Try / catch

err := client.Jobs().Register(job, nil, nil)
if err != nil && strings.Contains(err.Error(), "failed adding job") {
    // periodic spec could not be evaluated: fix cron/tz and resubmit
}

Prevention

When it happens

Trigger: job.Periodic.Next(time.Now().In(job.Periodic.GetLocation())) returns an error — e.g. a cron spec the time.Next parser cannot evaluate, or an invalid/unresolvable time location after job registration or at startup restore.

Common situations: Job submitted with a malformed cron expression that passed light validation but fails at computation; timezone string (tz field) that cannot be loaded via time.LoadLocation on the server; restoring jobs at startup whose spec is incompatible with the current Nomad version.

Related errors


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