{"record":{"id":"144eb0ab6705ca00","repo":"hashicorp/nomad","slug":"retry-config-backoff-d-is-greater-than-max-backof","errorCode":null,"errorMessage":"retry config backoff %d is greater than max_backoff %d","messagePattern":"retry config backoff (.+?) is greater than max_backoff (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/config/config.go","lineNumber":857,"sourceCode":"\n\t// If Backoff not set, no need to validate\n\tif rc.Backoff == nil {\n\t\treturn nil\n\t}\n\n\t// MaxBackoff nil will end up defaulted to 1 minutes. We should validate that\n\t// the user supplied backoff does not exceed that.\n\tif rc.MaxBackoff == nil && *rc.Backoff > config.DefaultRetryMaxBackoff {\n\t\treturn fmt.Errorf(\"retry config backoff %d is greater than default max_backoff %d\", *rc.Backoff, config.DefaultRetryMaxBackoff)\n\t}\n\n\t// MaxBackoff == 0 means backoff is unbounded. No need to validate.\n\tif rc.MaxBackoff != nil && *rc.MaxBackoff == 0 {\n\t\treturn nil\n\t}\n\n\tif rc.MaxBackoff != nil && *rc.Backoff > *rc.MaxBackoff {\n\t\treturn fmt.Errorf(\"retry config backoff %d is greater than max_backoff %d\", *rc.Backoff, *rc.MaxBackoff)\n\t}\n\n\treturn nil\n}\n\n// Merge merges two RetryConfigs. The passed instance always takes precedence.\nfunc (rc *RetryConfig) Merge(b *RetryConfig) *RetryConfig {\n\tif rc == nil {\n\t\treturn b\n\t}\n\n\tresult := *rc\n\tif b == nil {\n\t\treturn &result\n\t}\n\n\tif b.Attempts != nil {\n\t\tresult.Attempts = &*b.Attempts","sourceCodeStart":839,"sourceCodeEnd":875,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/config/config.go#L839-L875","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nretry {\n  backoff = \"5m\"\n  max_backoff = \"1m\"\n}\n// after\nretry {\n  backoff = \"30s\"\n  max_backoff = \"5m\"\n}","handlingStrategy":"validation","validationCode":"if rc.Backoff != nil && rc.MaxBackoff != nil && *rc.MaxBackoff != 0 && *rc.Backoff > *rc.MaxBackoff {\n    return fmt.Errorf(\"backoff %s > max_backoff %s\", *rc.Backoff, *rc.MaxBackoff)\n}","typeGuard":"func retryBoundsOK(rc *config.RetryConfig) bool {\n    return rc == nil || rc.Backoff == nil || rc.MaxBackoff == nil || *rc.MaxBackoff == 0 || *rc.Backoff <= *rc.MaxBackoff\n}","tryCatchPattern":"if err := rc.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"greater than max_backoff\") {\n        return fmt.Errorf(\"fix retry block: backoff must be <= max_backoff (or set max_backoff=0 for unbounded): %w\", err)\n    }\n    return err\n}","preventionTips":["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"],"tags":["config","validation","retry","backoff","go"],"backgroundTag":"invalid-config-value","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"}