hashicorp/nomad · error
retry config backoff %d is greater than max_backoff %d
Error message
retry config backoff %d is greater than max_backoff %d
What it means
RetryConfig.Validate rejects a RetryConfig whose Backoff exceeds its explicit MaxBackoff; setting max_backoff=0 disables the check because 0 means unbounded. The error embeds both raw nanosecond values so the offending pair is visible.
Source
Thrown at client/config/config.go:857
// If Backoff not set, no need to validate
if rc.Backoff == nil {
return nil
}
// MaxBackoff nil will end up defaulted to 1 minutes. We should validate that
// the user supplied backoff does not exceed that.
if rc.MaxBackoff == nil && *rc.Backoff > config.DefaultRetryMaxBackoff {
return fmt.Errorf("retry config backoff %d is greater than default max_backoff %d", *rc.Backoff, config.DefaultRetryMaxBackoff)
}
// MaxBackoff == 0 means backoff is unbounded. No need to validate.
if rc.MaxBackoff != nil && *rc.MaxBackoff == 0 {
return nil
}
if rc.MaxBackoff != nil && *rc.Backoff > *rc.MaxBackoff {
return fmt.Errorf("retry config backoff %d is greater than max_backoff %d", *rc.Backoff, *rc.MaxBackoff)
}
return nil
}
// Merge merges two RetryConfigs. The passed instance always takes precedence.
func (rc *RetryConfig) Merge(b *RetryConfig) *RetryConfig {
if rc == nil {
return b
}
result := *rc
if b == nil {
return &result
}
if b.Attempts != nil {
result.Attempts = &*b.AttemptsView on GitHub (pinned to 482b49bf1a)
Solutions
- Raise max_backoff to be >= backoff, e.g. backoff="30s" max_backoff="5m"
- Lower backoff below the configured max_backoff
- Set max_backoff = 0 explicitly if unbounded backoff is intended and the backoff value is deliberate
- Validate the merged RetryConfig after Merge to catch inversions introduced by overrides
Example fix
// before
retry {
backoff = "5m"
max_backoff = "1m"
}
// after
retry {
backoff = "30s"
max_backoff = "5m"
} Defensive patterns
Strategy: validation
Validate before calling
if rc.Backoff != nil && rc.MaxBackoff != nil && *rc.MaxBackoff != 0 && *rc.Backoff > *rc.MaxBackoff {
return fmt.Errorf("backoff %s > max_backoff %s", *rc.Backoff, *rc.MaxBackoff)
} Type guard
func retryBoundsOK(rc *config.RetryConfig) bool {
return rc == nil || rc.Backoff == nil || rc.MaxBackoff == nil || *rc.MaxBackoff == 0 || *rc.Backoff <= *rc.MaxBackoff
} Try / catch
if err := rc.Validate(); err != nil {
if strings.Contains(err.Error(), "greater than max_backoff") {
return fmt.Errorf("fix retry block: backoff must be <= max_backoff (or set max_backoff=0 for unbounded): %w", err)
}
return err
} Prevention
- Enforce backoff <= max_backoff (with max_backoff=0 meaning unbounded) in config review
- Re-validate RetryConfig after Merge since overrides can invert bounds
- Set max_backoff=0 deliberately only when unbounded backoff is intended
When it happens
Trigger: Calling RetryConfig.Validate when both Backoff and MaxBackoff are non-nil, MaxBackoff != 0, and *Backoff > *MaxBackoff, e.g. backoff="5m" max_backoff="1m"; also after Merge combines configs producing an inverted pair.
Common situations: retry block in agent config with max_backoff smaller than backoff; layered config merge where an override sets a lower max_backoff; programmatic construction setting fields independently without ordering checks.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- retry config backoff %d is greater than default max_backoff
- retry config is nil or empty
- wait config min %d is greater than max %d
- wait config is nil or empty
- fingerprint name cannot be empty
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/144eb0ab6705ca00.
Report an issue: GitHub.