{"record":{"id":"2984da64deb2a7e5","repo":"hashicorp/nomad","slug":"task-group-v-should-have-a-restart-policy","errorCode":null,"errorMessage":"Task Group %v should have a restart policy","messagePattern":"Task Group (.+?) should have a restart policy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":7184,"sourceCode":"\tif j.Type == JobTypeSystem {\n\t\tif tg.Affinities != nil {\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"System jobs may not have an affinity block\"))\n\t\t}\n\t} else {\n\t\tfor idx, affinity := range tg.Affinities {\n\t\t\tif err := affinity.Validate(); err != nil {\n\t\t\t\touter := fmt.Errorf(\"Affinity %d validation failed: %s\", idx+1, err)\n\t\t\t\tmErr = multierror.Append(mErr, outer)\n\t\t\t}\n\t\t}\n\t}\n\n\tif tg.RestartPolicy != nil {\n\t\tif err := tg.RestartPolicy.Validate(); err != nil {\n\t\t\tmErr = multierror.Append(mErr, err)\n\t\t}\n\t} else {\n\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Task Group %v should have a restart policy\", tg.Name))\n\t}\n\n\tif j.Type == JobTypeSystem {\n\t\tif tg.Spreads != nil {\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"System jobs may not have a spread block\"))\n\t\t}\n\t} else {\n\t\tfor idx, spread := range tg.Spreads {\n\t\t\tif err := spread.Validate(); err != nil {\n\t\t\t\touter := fmt.Errorf(\"Spread %d validation failed: %s\", idx+1, err)\n\t\t\t\tmErr = multierror.Append(mErr, outer)\n\t\t\t}\n\t\t}\n\t}\n\n\tif j.Type == JobTypeSystem || j.Type == JobTypeSysBatch {\n\t\tif tg.ReschedulePolicy != nil {\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"System or sysbatch jobs should not have a reschedule policy\"))","sourceCodeStart":7166,"sourceCodeEnd":7202,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L7166-L7202","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ngroup \"web\" {\n  count = 2\n  // no restart policy\n}\n// after\ngroup \"web\" {\n  count = 2\n  restart {\n    attempts = 2\n    interval = \"30m\"\n    delay    = \"15s\"\n    mode     = \"fail\"\n  }\n}","handlingStrategy":"validation","validationCode":"func ensureRestartPolicy(tg map[string]interface{}) error {\n  if _, ok := tg[\"restart\"]; !ok {\n    tg[\"restart\"] = map[string]interface{}{\n      \"attempts\": 2, \"interval\": \"30m\", \"delay\": \"15s\", \"mode\": \"fail\",\n    }\n  }\n  return nil\n}","typeGuard":"func hasRestartPolicy(tg *api.TaskGroup) bool { return tg != nil && tg.RestartPolicy != nil }","tryCatchPattern":"if err := job.Validate(); err != nil {\n  if strings.Contains(err.Error(), \"should have a restart policy\") {\n    return fmt.Errorf(\"call Canonicalize() or add a restart block: %w\", err)\n  }\n  return err\n}","preventionTips":["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."],"tags":["nomad","job-validation","restart-policy","canonicalize"],"backgroundTag":"job-spec-validation-failed","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}