{"record":{"id":"637f4dd2fb40c05b","repo":"hashicorp/nomad","slug":"nomad-can-only-make-v-attempts-in-v-with-initial-637f4d","errorCode":null,"errorMessage":"Nomad can only make %v attempts in %v with initial delay %v, delay function %q, and delay ceiling %v","messagePattern":"Nomad can only make (.+?) attempts in (.+?) with initial delay (.+?), delay function %q, and delay ceiling (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":6726,"sourceCode":"\tfor _, value := range RescheduleDelayFunctions {\n\t\tif value == delayFunc {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (r *ReschedulePolicy) validateDelayParams() error {\n\tok, possibleAttempts, recommendedInterval := r.viableAttempts()\n\tif ok {\n\t\treturn nil\n\t}\n\tvar mErr multierror.Error\n\tif r.DelayFunction == \"constant\" {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Nomad can only make %v attempts in %v with initial delay %v and \"+\n\t\t\t\"delay function %q\", possibleAttempts, r.Interval, r.Delay, r.DelayFunction))\n\t} else {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Nomad can only make %v attempts in %v with initial delay %v, \"+\n\t\t\t\"delay function %q, and delay ceiling %v\", possibleAttempts, r.Interval, r.Delay, r.DelayFunction, r.MaxDelay))\n\t}\n\t_ = multierror.Append(&mErr, fmt.Errorf(\"Set the interval to at least %v to accommodate %v attempts\", recommendedInterval.Round(time.Second), r.Attempts))\n\treturn mErr.ErrorOrNil()\n}\n\nfunc (r *ReschedulePolicy) viableAttempts() (bool, int, time.Duration) {\n\tvar possibleAttempts int\n\tvar recommendedInterval time.Duration\n\tvalid := true\n\tswitch r.DelayFunction {\n\tcase \"constant\":\n\t\trecommendedInterval = time.Duration(r.Attempts) * r.Delay\n\t\tif r.Interval < recommendedInterval {\n\t\t\tpossibleAttempts = int(r.Interval / r.Delay)\n\t\t\tvalid = false\n\t\t}\n\tcase \"exponential\":","sourceCodeStart":6708,"sourceCodeEnd":6744,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L6708-L6744","documentation":"Same viability failure as the constant-delay case, but thrown when delay_function is fibonacci or exponential. viableAttempts() simulates the growing delays (bounded by MaxDelay) and finds that fewer than Attempts retries fit within Interval. The message includes the delay ceiling (MaxDelay) because growth saturation affects the attempt math.","triggerScenarios":"Submitting a job with reschedule.delay_function = \"exponential\" or \"fibonacci\" where the cumulative delays (each capped at MaxDelay) summed over Attempts exceed Interval, with unlimited=false.","commonSituations":"Large initial delay with exponential growth and a tight interval; MaxDelay set very high so delays keep growing past what the interval can hold; misconfigured policies where interval was chosen for constant-delay semantics.","solutions":["Increase interval to at least the recommendedInterval value printed by the companion error.","Lower the initial delay and/or MaxDelay so growing delays fit within the interval.","Reduce attempts to a count that fits the simulated delay schedule.","Switch delay_function to \"constant\" and size interval >= attempts * delay.","Set unlimited: true to bypass viability checks."],"exampleFix":"// before\nreschedule {\n  attempts      = 5\n  interval      = \"30m\"\n  delay         = \"10m\"\n  delay_function = \"exponential\"\n  max_delay     = \"1h\"\n}\n// after\nreschedule {\n  attempts      = 5\n  interval      = \"8h\"\n  delay         = \"30s\"\n  delay_function = \"exponential\"\n  max_delay     = \"1h\"\n}","handlingStrategy":"validation","validationCode":"func growingDelayViable(p *structs.ReschedulePolicy) bool {\n\tif p.Unlimited || p.DelayFunction == \"constant\" {\n\t\treturn true\n\t}\n\tdelay, total := p.Delay, time.Duration(0)\n\tfor i := 0; i < p.Attempts; i++ {\n\t\tif delay > p.MaxDelay {\n\t\t\tdelay = p.MaxDelay\n\t\t}\n\t\ttotal += delay\n\t\tdelay *= 2 // exponential; use fibonacci series for \"fibonacci\"\n\t}\n\treturn total <= p.Interval\n}","typeGuard":"func growingDelayFits(p *structs.ReschedulePolicy) bool {\n\tok, _, _ := p.ViableAttempts() // or replicate viableAttempts() logic\n\treturn p.Unlimited || ok\n}","tryCatchPattern":"if err := policy.Validate(); err != nil {\n\tif strings.Contains(err.Error(), \"delay ceiling\") {\n\t\t// lower delay/max_delay or raise interval before resubmit\n\t}\n}","preventionTips":["Model cumulative growing delays (bounded by max_delay) before choosing interval.","Start with small initial delays for exponential/fibonacci functions.","Cap max_delay so total retry time stays within interval.","Run policy validation in CI for generated job specs."],"tags":["nomad","go","validation","reschedule-policy","configuration"],"backgroundTag":"reschedule-attempts-exceed-interval","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"}