hashicorp/nomad · error
Task Group %v should have a restart policy
Error message
Task Group %v should have a restart policy
What it means
Nomad task group validation (nomad/structs/structs.go) requires every task group to have a restart policy. If tg.RestartPolicy is nil after job submission (the API normally interpolates a default), the validator appends "Task Group %v should have a restart policy" naming the group, since restart behavior is mandatory for scheduling semantics.
Source
Thrown at nomad/structs/structs.go:7184
if j.Type == JobTypeSystem {
if tg.Affinities != nil {
mErr = multierror.Append(mErr, fmt.Errorf("System jobs may not have an affinity block"))
}
} else {
for idx, affinity := range tg.Affinities {
if err := affinity.Validate(); err != nil {
outer := fmt.Errorf("Affinity %d validation failed: %s", idx+1, err)
mErr = multierror.Append(mErr, outer)
}
}
}
if tg.RestartPolicy != nil {
if err := tg.RestartPolicy.Validate(); err != nil {
mErr = multierror.Append(mErr, err)
}
} else {
mErr = multierror.Append(mErr, fmt.Errorf("Task Group %v should have a restart policy", tg.Name))
}
if j.Type == JobTypeSystem {
if tg.Spreads != nil {
mErr = multierror.Append(mErr, fmt.Errorf("System jobs may not have a spread block"))
}
} else {
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"))View on GitHub (pinned to 482b49bf1a)
Solutions
- Set a restart policy on the task group (or submit through the HTTP API / `nomad job run`, which canonicalizes and fills defaults).
- Call Job.Canonicalize() (or the API job's Canonicalize) before validation to materialize default restart policies.
- If hand-writing JSON, include a `RestartPolicy` object with Interval, Attempts, Delay, and Mode fields.
Example fix
// before
group "web" {
count = 2
// no restart policy
}
// after
group "web" {
count = 2
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
} Defensive patterns
Strategy: validation
Validate before calling
func ensureRestartPolicy(tg map[string]interface{}) error {
if _, ok := tg["restart"]; !ok {
tg["restart"] = map[string]interface{}{
"attempts": 2, "interval": "30m", "delay": "15s", "mode": "fail",
}
}
return nil
} Type guard
func hasRestartPolicy(tg *api.TaskGroup) bool { return tg != nil && tg.RestartPolicy != nil } Try / catch
if err := job.Validate(); err != nil {
if strings.Contains(err.Error(), "should have a restart policy") {
return fmt.Errorf("call Canonicalize() or add a restart block: %w", err)
}
return err
} Prevention
- Always submit jobs through the HTTP API or CLI so defaults are canonicalized.
- Call Canonicalize() on jobs built with the Go SDK before Validate/submit.
- Include a restart block in every task-group template snippet.
When it happens
Trigger: Constructing a structs.TaskGroup programmatically (Go API or raw JSON job submission) without setting RestartPolicy, bypassing the API layer that normally fills in defaults; submitting a group with restart {} stripped out and no policy materialized.
Common situations: Building jobs via the Nomad Go SDK/structs package directly; crafting minimal JSON job payloads by hand for `nomad job run` API calls; version drift where a tool deletes restart blocks assuming server-side defaulting that only happens through the HTTP API canonicalization.
Related errors
- ErrConnectRequireOneNetwork
- ErrConnectInvalidNetworkMode
- service.port must be set for mesh gateway service
- non-default Consul cluster requires Nomad Enterprise
- numa scheduling requires Nomad Enterprise
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/2984da64deb2a7e5.
Report an issue: GitHub.