{"record":{"id":"80421e99b170773c","repo":"hashicorp/nomad","slug":"task-group-v-should-have-a-reschedule-policy","errorCode":null,"errorMessage":"Task Group %v should have a reschedule policy","messagePattern":"Task Group (.+?) should have a reschedule policy","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":7210,"sourceCode":"\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\"))\n\t\t}\n\t} else {\n\t\tif tg.ReschedulePolicy != nil {\n\t\t\tif err := tg.ReschedulePolicy.Validate(); err != nil {\n\t\t\t\tmErr = multierror.Append(mErr, err)\n\t\t\t}\n\t\t} else {\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Task Group %v should have a reschedule policy\", tg.Name))\n\t\t}\n\t}\n\n\tif tg.EphemeralDisk != nil {\n\t\tif err := tg.EphemeralDisk.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 an ephemeral disk object\", tg.Name))\n\t}\n\n\t// Validate the update strategy\n\tif u := tg.Update; u != nil {\n\t\tswitch j.Type {\n\t\tcase JobTypeService, JobTypeSystem:\n\t\tdefault:\n\t\t\tmErr = multierror.Append(mErr, fmt.Errorf(\"Job type %q does not allow update block\", j.Type))\n\t\t}","sourceCodeStart":7192,"sourceCodeEnd":7228,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L7192-L7228","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Add an explicit `reschedule` stanza to the task group in the job spec.","If using the Go API, call `job.Canonicalize()` (or submit via the HTTP API, which fills defaults) before validation.","Verify the job type is intentional — system/sysbatch jobs are exempt and must not set reschedule."],"exampleFix":"// before\ngroup \"web\" {\n  count = 3\n}\n// after\ngroup \"web\" {\n  count = 3\n  reschedule {\n    attempts  = 2\n    interval  = \"30m\"\n    delay     = \"30s\"\n    max_delay = \"1h\"\n    unlimited = false\n  }\n}","handlingStrategy":"validation","validationCode":"func ensureReschedulePolicy(j *api.Job) {\n\tfor _, tg := range j.TaskGroups {\n\t\tif tg.ReschedulePolicy == nil || *tg.ReschedulePolicy == (api.ReschedulePolicy{}) {\n\t\t\ttg.ReschedulePolicy = &api.ReschedulePolicy{\n\t\t\t\tAttempts:  pointer.Of(2),\n\t\t\t\tInterval:  pointer.Of(30 * time.Minute),\n\t\t\t\tDelay:     pointer.Of(30 * time.Second),\n\t\t\t\tDelayFunc: pointer.Of(\"constant\"),\n\t\t\t\tMaxDelay:  pointer.Of(1 * time.Hour),\n\t\t\t\tUnlimited: pointer.Of(false),\n\t\t\t}\n\t\t}\n\t}\n}","typeGuard":"func hasReschedule(tg *api.TaskGroup) bool { return tg != nil && tg.ReschedulePolicy != nil && *tg.ReschedulePolicy != (api.ReschedulePolicy{}) }","tryCatchPattern":null,"preventionTips":["Always call `job.Canonicalize()` on jobs built with the Go API before submission.","Prefer the HTTP API/CLI which fills defaults server-side.","Include reschedule stanzas explicitly in job templates to avoid reliance on defaults."],"tags":["nomad","job-validation","reschedule","missing-config"],"backgroundTag":"missing-required-argument","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"}