{"record":{"id":"e6b7e542e861e8c8","repo":"hashicorp/nomad","slug":"if-attempts-0-unlimited-cannot-also-be-set-to-tr","errorCode":null,"errorMessage":"If Attempts >0, Unlimited cannot also be set to true","messagePattern":"If Attempts >0, Unlimited cannot also be set to true","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":6660,"sourceCode":"\n// Validate uses different criteria to validate the reschedule policy\n// Delay must be a minimum of 1 second\n// Delay Ceiling is ignored if Delay Function is \"constant\"\n// Number of possible attempts is validated, given the interval, delay and delay function\nfunc (r *ReschedulePolicy) Validate() error {\n\tif !r.Enabled() {\n\t\treturn nil\n\t}\n\tvar mErr multierror.Error\n\t// Check for ambiguous/confusing settings\n\tif r.Attempts > 0 {\n\t\tif r.Interval <= 0 {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Interval must be a non zero value if Attempts > 0\"))\n\t\t}\n\t\tif r.Unlimited {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Reschedule Policy with Attempts = %v, Interval = %v, \"+\n\t\t\t\t\"and Unlimited = %v is ambiguous\", r.Attempts, r.Interval, r.Unlimited))\n\t\t\t_ = multierror.Append(&mErr, errors.New(\"If Attempts >0, Unlimited cannot also be set to true\"))\n\t\t}\n\t}\n\n\tdelayPreCheck := true\n\t// Delay should be bigger than the default\n\tif r.Delay.Nanoseconds() < ReschedulePolicyMinDelay.Nanoseconds() {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Delay cannot be less than %v (got %v)\", ReschedulePolicyMinDelay, r.Delay))\n\t\tdelayPreCheck = false\n\t}\n\n\t// Must use a valid delay function\n\tif !isValidDelayFunction(r.DelayFunction) {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Invalid delay function %q, must be one of %q\", r.DelayFunction, RescheduleDelayFunctions))\n\t\tdelayPreCheck = false\n\t}\n\n\t// Validate MaxDelay if not using linear delay progression\n\tif r.DelayFunction != \"constant\" {","sourceCodeStart":6642,"sourceCodeEnd":6678,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L6642-L6678","documentation":"A reschedule policy that sets Unlimited=true together with Attempts>0 is contradictory: unlimited rescheduling means no attempt cap. ValidateReschedulePolicy in nomad/structs appends this error when r.Attempts > 0 and r.Unlimited are both set.","triggerScenarios":"Submitting a job whose group reschedule policy has attempts > 0 and unlimited = true; constructing structs.ReschedulePolicy directly with both fields set.","commonSituations":"Merging config fragments where one sets unlimited=true and another sets attempts; copying a sample policy and toggling both knobs; upgrading specs written before the validation existed.","solutions":["Set unlimited = false (default) so attempts/interval take effect.","Or remove attempts and interval, leaving unlimited = true.","Use nomad job validate to check the spec before submission."],"exampleFix":"// before\nreschedule {\n  attempts  = 5\n  interval  = \"30m\"\n  unlimited = true\n}\n// after\nreschedule {\n  attempts = 5\n  interval = \"30m\"\n}","handlingStrategy":"validation","validationCode":"if rp.Attempts > 0 && rp.Unlimited {\n    return errors.New(\"reschedule policy: attempts>0 and unlimited=true are mutually exclusive\")\n}","typeGuard":"func isUnambiguousReschedule(r *structs.ReschedulePolicy) bool { return !r.Unlimited || r.Attempts == 0 }","tryCatchPattern":null,"preventionTips":["Pick one mode: either unlimited=true OR attempts+interval","Never merge reschedule policy fragments from different sources","Run nomad job validate in CI for every job spec"],"tags":["nomad","reschedule-policy","config-validation"],"backgroundTag":"conflicting-config-options","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}