hashicorp/nomad · error

Task Group %v should have a reschedule policy

Error message

Task Group %v should have a reschedule policy

What it means

Nomad requires every task group of a non-system/non-sysbatch job (service, batch) to have a reschedule policy. When `tg.ReschedulePolicy == nil` for such a group, validation appends this error naming the group. This usually means the job spec was constructed in code without `Canonicalize()` being applied, or the HCL omitted the stanza while bypassing canonicalization defaults.

Source

Thrown at nomad/structs/structs.go:7210

		for idx, spread := range tg.Spreads {
			if err := spread.Validate(); err != nil {
				outer := fmt.Errorf("Spread %d validation failed: %s", idx+1, err)
				mErr = multierror.Append(mErr, outer)
			}
		}
	}

	if j.Type == JobTypeSystem || j.Type == JobTypeSysBatch {
		if tg.ReschedulePolicy != nil {
			mErr = multierror.Append(mErr, fmt.Errorf("System or sysbatch jobs should not have a reschedule policy"))
		}
	} else {
		if tg.ReschedulePolicy != nil {
			if err := tg.ReschedulePolicy.Validate(); err != nil {
				mErr = multierror.Append(mErr, err)
			}
		} else {
			mErr = multierror.Append(mErr, fmt.Errorf("Task Group %v should have a reschedule policy", tg.Name))
		}
	}

	if tg.EphemeralDisk != nil {
		if err := tg.EphemeralDisk.Validate(); err != nil {
			mErr = multierror.Append(mErr, err)
		}
	} else {
		mErr = multierror.Append(mErr, fmt.Errorf("Task Group %v should have an ephemeral disk object", tg.Name))
	}

	// Validate the update strategy
	if u := tg.Update; u != nil {
		switch j.Type {
		case JobTypeService, JobTypeSystem:
		default:
			mErr = multierror.Append(mErr, fmt.Errorf("Job type %q does not allow update block", j.Type))
		}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Add an explicit `reschedule` stanza to the task group in the job spec.
  2. If using the Go API, call `job.Canonicalize()` (or submit via the HTTP API, which fills defaults) before validation.
  3. Verify the job type is intentional — system/sysbatch jobs are exempt and must not set reschedule.

Example fix

// before
group "web" {
  count = 3
}
// after
group "web" {
  count = 3
  reschedule {
    attempts  = 2
    interval  = "30m"
    delay     = "30s"
    max_delay = "1h"
    unlimited = false
  }
}
Defensive patterns

Strategy: validation

Validate before calling

func ensureReschedulePolicy(j *api.Job) {
	for _, tg := range j.TaskGroups {
		if tg.ReschedulePolicy == nil || *tg.ReschedulePolicy == (api.ReschedulePolicy{}) {
			tg.ReschedulePolicy = &api.ReschedulePolicy{
				Attempts:  pointer.Of(2),
				Interval:  pointer.Of(30 * time.Minute),
				Delay:     pointer.Of(30 * time.Second),
				DelayFunc: pointer.Of("constant"),
				MaxDelay:  pointer.Of(1 * time.Hour),
				Unlimited: pointer.Of(false),
			}
		}
	}
}

Type guard

func hasReschedule(tg *api.TaskGroup) bool { return tg != nil && tg.ReschedulePolicy != nil && *tg.ReschedulePolicy != (api.ReschedulePolicy{}) }

Prevention

When it happens

Trigger: Registering a service or batch job whose task group has no `reschedule` stanza and whose structs were not canonicalized (API submissions normally get defaults filled in; raw struct construction or partial API payloads can skip that).

Common situations: Building jobs programmatically via the Go API and forgetting `job.Canonicalize()`; stripped-down JSON job payloads omitting defaults; test fixtures built by hand without reschedule policy.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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