hashicorp/nomad · error
Restart policy %q with %d attempts is ambiguous
Error message
Restart policy %q with %d attempts is ambiguous
What it means
RestartPolicy.Validate() flags a policy where attempts is 0 but mode is not "fail". With mode "delay" and zero attempts Nomad would never restart within the window, making the interval/attempts block meaningless and ambiguous. Nomad refuses to guess the intended behavior.
Source
Thrown at nomad/structs/structs.go:6575
if r == nil {
return nil
}
nrp := new(RestartPolicy)
*nrp = *r
return nrp
}
func (r *RestartPolicy) Validate() error {
var mErr multierror.Error
switch r.Mode {
case RestartPolicyModeDelay, RestartPolicyModeFail:
default:
_ = multierror.Append(&mErr, fmt.Errorf("Unsupported restart mode: %q", r.Mode))
}
// Check for ambiguous/confusing settings
if r.Attempts == 0 && r.Mode != RestartPolicyModeFail {
_ = multierror.Append(&mErr, fmt.Errorf("Restart policy %q with %d attempts is ambiguous", r.Mode, r.Attempts))
}
if r.Interval.Nanoseconds() < RestartPolicyMinInterval.Nanoseconds() {
_ = multierror.Append(&mErr, fmt.Errorf("Interval can not be less than %v (got %v)", RestartPolicyMinInterval, r.Interval))
}
if time.Duration(r.Attempts)*r.Delay > r.Interval {
_ = multierror.Append(&mErr,
fmt.Errorf("Nomad can't restart the TaskGroup %v times in an interval of %v with a delay of %v", r.Attempts, r.Interval, r.Delay))
}
return mErr.ErrorOrNil()
}
func NewRestartPolicy(jobType string) *RestartPolicy {
switch jobType {
case JobTypeService, JobTypeSystem:
rp := DefaultServiceJobRestartPolicy
return &rp
case JobTypeBatch:View on GitHub (pinned to 482b49bf1a)
Solutions
- Set attempts to a positive integer (e.g. attempts = 2) if you want restarts.
- If you truly want no restarts on failure, set mode = "fail" so zero attempts is valid.
- Omit the restart block entirely to use the job-type default policy.
Example fix
// before
restart {
interval = "30m"
attempts = 0
mode = "delay"
}
// after
restart {
interval = "30m"
attempts = 2
mode = "delay"
} Defensive patterns
Strategy: validation
Validate before calling
if rp != nil && rp.Attempts == 0 && rp.Mode != "fail" {
return fmt.Errorf("restart attempts=0 requires mode=fail")
} Type guard
func restartPolicySane(rp *structs.RestartPolicy) bool {
return rp.Mode == "fail" || rp.Attempts > 0
} Prevention
- Always pair attempts > 0 with mode = "delay".
- Use NewRestartPolicy(jobType) to get sane defaults instead of hand-building structs.
- Remember attempts=0 does not mean unlimited.
When it happens
Trigger: A task group restart block with attempts = 0 (explicitly or via zero-value struct) while mode is "delay"; also common when interval/attempts are set programmatically and attempts defaults to 0.
Common situations: Hand-written job files that set interval/delay but forget attempts; templating code that renders an empty attempts field; users expecting attempts=0 to mean 'unlimited' (it does not).
Related errors
- Unsupported restart mode: %q
- 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
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/b61a494060a87995.
Report an issue: GitHub.