{"id":"5e4cb47460c0148e","repo":"go-redis/redis","slug":"connection-marked-for-handoff","errorCode":null,"errorMessage":"connection marked for handoff","messagePattern":"connection marked for handoff","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"info","filePath":"maintnotifications/errors.go","lineNumber":48,"sourceCode":")\n\n// Handoff errors\nvar (\n\t// ErrHandoffQueueFull is returned when the handoff queue is full\n\tErrHandoffQueueFull = errors.New(logs.HandoffQueueFullError())\n)\n\n// Notification errors\nvar (\n\t// ErrInvalidNotification is returned when a notification is in an invalid format\n\tErrInvalidNotification = errors.New(logs.InvalidNotificationError())\n)\n\n// connection handoff errors\nvar (\n\t// ErrConnectionMarkedForHandoff is returned when a connection is marked for handoff\n\t// and should not be used until the handoff is complete\n\tErrConnectionMarkedForHandoff = errors.New(logs.ConnectionMarkedForHandoffErrorMessage)\n\t// ErrConnectionMarkedForHandoffWithState is returned when a connection is marked for handoff\n\t// and should not be used until the handoff is complete\n\tErrConnectionMarkedForHandoffWithState = errors.New(logs.ConnectionMarkedForHandoffErrorMessage + \" with state\")\n\t// ErrConnectionInvalidHandoffState is returned when a connection is in an invalid state for handoff\n\tErrConnectionInvalidHandoffState = errors.New(logs.ConnectionInvalidHandoffStateErrorMessage)\n)\n\n// shutdown errors\nvar (\n\t// ErrShutdown is returned when the maintnotifications manager is shutdown\n\tErrShutdown = errors.New(logs.ShutdownError())\n)\n\n// circuit breaker errors\nvar (\n\t// ErrCircuitBreakerOpen is returned when the circuit breaker is open\n\tErrCircuitBreakerOpen = errors.New(logs.CircuitBreakerOpenErrorMessage)\n)","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/maintnotifications/errors.go#L30-L66","documentation":"Returned by PoolHook.OnGet (pool_hook.go:133) when a pooled connection is not IsUsable() — typically because it is mid-handoff, being re-authenticated, or otherwise in an UNUSABLE/CLOSED state. It signals the pool to skip this connection and pick another; it is a normal flow-control signal during maintenance, not necessarily a caller-visible failure.","triggerScenarios":"The pool's Get path calls OnGet; a connection that was previously returned to the pool is now unusable (a handoff worker is actively relocating it, or it was marked closed). OnGet returns (false, ErrConnectionMarkedForHandoff) so the pool retries with a different connection.","commonSituations":"Transient during a live migration/failover; can surface as an error to the application only if most/all connections in the pool are simultaneously unusable (small PoolSize during a storm).","solutions":["Increase PoolSize so the pool has enough usable connections during handoff churn.","Confirm MaxHandoffRetries/HandoffTimeout aren't leaving connections stuck unusable for long.","If your pool wrapper surfaces OnGet errors directly to callers, translate this sentinel into a retry-on-another-connection rather than a hard failure.","Check for an unhealthy upstream causing repeated re-auth or handoff (circuit breaker stats)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"// This is normally handled inside the pool (it picks another conn).\n// If surfacing from a custom hook wrapper, retry the command:\nif errors.Is(err, maintnotifications.ErrConnectionMarkedForHandoff) {\n    return retryCommand(ctx, cmd)\n}","preventionTips":["Raise PoolSize so unusable connections don't exhaust the pool.","In custom pool wrappers, treat this sentinel as 'pick another connection', not fatal.","Monitor how long connections stay unusable."],"tags":["maintnotifications","pool","handoff","connection"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}