{"id":"822f7c27b2eab1c5","repo":"go-redis/redis","slug":"post-handoff-relaxed-duration-must-be-greater-than","errorCode":null,"errorMessage":"post-handoff relaxed duration must be greater than or equal to 0","messagePattern":"post-handoff relaxed duration must be greater than or equal to 0","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"maintnotifications/errors.go","lineNumber":14,"sourceCode":"package maintnotifications\n\nimport (\n\t\"errors\"\n\n\t\"github.com/redis/go-redis/v9/internal/maintnotifications/logs\"\n)\n\n// Configuration errors\nvar (\n\tErrInvalidRelaxedTimeout             = errors.New(logs.InvalidRelaxedTimeoutError())\n\tErrInvalidHandoffTimeout             = errors.New(logs.InvalidHandoffTimeoutError())\n\tErrInvalidHandoffWorkers             = errors.New(logs.InvalidHandoffWorkersError())\n\tErrInvalidHandoffQueueSize           = errors.New(logs.InvalidHandoffQueueSizeError())\n\tErrInvalidPostHandoffRelaxedDuration = errors.New(logs.InvalidPostHandoffRelaxedDurationError())\n\tErrInvalidEndpointType               = errors.New(logs.InvalidEndpointTypeError())\n\tErrInvalidMaintNotifications         = errors.New(logs.InvalidMaintNotificationsError())\n\tErrMaxHandoffRetriesReached          = errors.New(logs.MaxHandoffRetriesReachedError())\n\n\t// Configuration validation errors\n\n\t// ErrInvalidHandoffRetries is returned when the number of handoff retries is invalid\n\tErrInvalidHandoffRetries = errors.New(logs.InvalidHandoffRetriesError())\n)\n\n// Integration errors\nvar (\n\t// ErrInvalidClient is returned when the client does not support push notifications\n\tErrInvalidClient = errors.New(logs.InvalidClientError())\n)\n\n// Handoff errors","sourceCodeStart":1,"sourceCodeEnd":32,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/maintnotifications/errors.go#L1-L32","documentation":"Returned by Config.Validate() (maintnotifications/config.go:173-175) when Config.PostHandoffRelaxedDuration is negative. This duration keeps relaxed read/write timeouts on a freshly handed-off connection to absorb latency during cluster transitions (handoff_worker.go). Zero is valid and means 'use the default of 2 * RelaxedTimeout'.","triggerScenarios":"Setting Options.MaintNotificationsConfig.PostHandoffRelaxedDuration to a negative time.Duration, then building the client which runs Validate().","commonSituations":"Subtracting durations in config that can yield a negative result (e.g. handoffTimeout - margin when margin is larger); passing a parsed negative number with a time unit; treating -1s as a 'disabled' flag.","solutions":["Set PostHandoffRelaxedDuration to 0 to use the default (2 * RelaxedTimeout, config.go:286).","Set it to an explicit non-negative duration you measured acceptable for post-handoff jitter.","Guard any computed duration with a `if d < 0 { d = 0 }` clamp before assignment."],"exampleFix":"// before\ncfg := &maintnotifications.Config{PostHandoffRelaxedDuration: -2 * time.Second}\n\n// after\ncfg := &maintnotifications.Config{PostHandoffRelaxedDuration: 0} // default","handlingStrategy":"validation","validationCode":"if cfg.PostHandoffRelaxedDuration < 0 {\n    cfg.PostHandoffRelaxedDuration = 0 // -> 2 * RelaxedTimeout\n}\nif err := cfg.Validate(); err != nil {\n    return fmt.Errorf(\"maint config: %w\", err)\n}","typeGuard":"func validPostHandoffDuration(d time.Duration) bool { return d >= 0 }","tryCatchPattern":null,"preventionTips":["Clamp any computed duration to >=0.","Leave it 0 unless you have measured post-handoff latency.","Validate all time.Duration maint fields together."],"tags":["maintnotifications","config","validation","timeout"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}