hashicorp/nomad · warning

Reschedule policy has unlimited attempts enabled and a low d

Error message

Reschedule policy has unlimited attempts enabled and a low delay; reschedule thrashing possible

What it means

TaskGroup.Validate warns when the group's reschedule policy enables Unlimited attempts while Delay is under 5 seconds. Unlimited fast retries can cause reschedule thrashing, repeatedly restarting the allocation in a tight loop. It is a warning-level validation error appended to the group multierror.

Source

Thrown at nomad/structs/structs.go:7673

	if tg.StopAfterClientDisconnect != nil {
		mErr.Errors = append(mErr.Errors, errors.New("StopAfterClientDisconnect is deprecated and ignored favor of Disconnect.StopOnClientAfter"))
	}

	if tg.PreventRescheduleOnLost {
		mErr.Errors = append(mErr.Errors, errors.New("PreventRescheduleOnLost is deprecated and ignored in favor of Disconnect.Replace"))
	}

	// Warn about unbounded rescheduling which may cause thrashing if tasks have
	// unlimited attempts and a low delay.
	//
	// The 5 second gate on the delay is used as this was the previous minimum
	// value which did not produce a warning. It therefore feels like the right
	// gate to use.
	if rp := tg.ReschedulePolicy; rp != nil && rp.Unlimited && rp.Delay < 5*time.Second {
		mErr.Errors = append(
			mErr.Errors,
			errors.New("Reschedule policy has unlimited attempts enabled and a low delay; reschedule thrashing possible"),
		)
	}

	// Check for mbits network field
	if len(tg.Networks) > 0 && tg.Networks[0].MBits > 0 {
		mErr.Errors = append(mErr.Errors, fmt.Errorf("mbits has been deprecated as of Nomad 0.12.0. Please remove mbits from the network block"))
	}

	// Validate group-level services.
	for _, s := range tg.Services {
		if err := s.Warnings(); err != nil {
			err = multierror.Prefix(err, fmt.Sprintf("Service %q:", s.Name))
			mErr = *multierror.Append(&mErr, err)
		}
	}

	for _, t := range tg.Tasks {
		if err := t.Warnings(); err != nil {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set reschedule delay >= 5s, e.g. delay = "30s".
  2. Or disable unlimited: `reschedule { unlimited = false, attempts = 5 }`.
  3. Or remove the reschedule block entirely to use sensible defaults.

Example fix

// before
reschedule {
  unlimited = true
  delay     = "1s"
}
// after
reschedule {
  unlimited = false
  attempts  = 5
  delay     = "30s"
  max_delay = "1h"
}
Defensive patterns

Strategy: validation

Validate before calling

if rp := tg.ReschedulePolicy; rp != nil && rp.Unlimited && rp.Delay < 5*time.Second {
  log.Warn("unlimited reschedule with low delay may thrash")
}

Type guard

func isThrashRisk(rp *structs.ReschedulePolicy) bool {
  return rp != nil && rp.Unlimited && rp.Delay < 5*time.Second
}

Prevention

When it happens

Trigger: A group has `reschedule { unlimited = true, delay = "1s" }` (Delay < 5s and DelayFunction leading to low delays) — either set explicitly or inherited from a namespace/agent default when the job overrides the policy.

Common situations: Hand-written reschedule blocks with very small delays; copied examples tuned for batch jobs but applied to services that will never become healthy.

Related errors


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