go-redis/redis · error
connection is in invalid state for handoff
Error message
connection is in invalid state for handoff
What it means
Returned by performConnectionHandoff (handoff_worker.go:364) when conn.GetHandoffEndpoint() returns an empty string. A handoff was initiated for a connection, but no target endpoint was recorded, so there is nowhere to relocate it; shouldRetry=false so the connection is dropped rather than retried.
Source
Thrown at maintnotifications/errors.go:53
ErrHandoffQueueFull = errors.New(logs.HandoffQueueFullError()) ) // Notification errors var ( // ErrInvalidNotification is returned when a notification is in an invalid format ErrInvalidNotification = errors.New(logs.InvalidNotificationError()) ) // connection handoff errors var ( // ErrConnectionMarkedForHandoff is returned when a connection is marked for handoff // and should not be used until the handoff is complete ErrConnectionMarkedForHandoff = errors.New(logs.ConnectionMarkedForHandoffErrorMessage) // ErrConnectionMarkedForHandoffWithState is returned when a connection is marked for handoff // and should not be used until the handoff is complete ErrConnectionMarkedForHandoffWithState = errors.New(logs.ConnectionMarkedForHandoffErrorMessage + " with state") // ErrConnectionInvalidHandoffState is returned when a connection is in an invalid state for handoff ErrConnectionInvalidHandoffState = errors.New(logs.ConnectionInvalidHandoffStateErrorMessage) ) // shutdown errors var ( // ErrShutdown is returned when the maintnotifications manager is shutdown ErrShutdown = errors.New(logs.ShutdownError()) ) // circuit breaker errors var ( // ErrCircuitBreakerOpen is returned when the circuit breaker is open ErrCircuitBreakerOpen = errors.New(logs.CircuitBreakerOpenErrorMessage) ) // circuit breaker configuration errors var ( // ErrInvalidCircuitBreakerFailureThreshold is returned when the circuit breaker failure threshold is invalid ErrInvalidCircuitBreakerFailureThreshold = errors.New(logs.InvalidCircuitBreakerFailureThresholdError())
View on GitHub (pinned to 36d97525cd)
Solutions
- Capture the full MOVING payload from logs (logs.InvalidNewEndpointInMovingNotification / InvalidNotification) to confirm the server sent an endpoint.
- Check for any custom code that calls conn handoff-state setters and remove it; state should only be driven by the notification handler.
- Upgrade go-redis — an empty endpoint at handoff time usually indicates a state-machine race fixed in recent versions.
- If reproducing, file an issue with the conn ID and surrounding handoff log lines.
Defensive patterns
Strategy: fallback
Try / catch
if errors.Is(err, maintnotifications.ErrConnectionInvalidHandoffState) {
// connection dropped; the next command uses a fresh connection.
// If recurring, capture MOVING payloads and report a possible state race.
return retryCommand(ctx, cmd)
} Prevention
- Do not manipulate pool.Conn handoff state from application code.
- Keep go-redis current — state-machine races are fixed in releases.
- Reproduce with full handoff logs (conn ID) before reporting.
When it happens
Trigger: A MOVING notification with a nil/empty newEndpoint led to a handoff being queued, but the connection's handoff endpoint field was never populated (or was cleared) by the time the worker ran performConnectionHandoff. This is an unexpected internal state — MOVING normally guarantees an endpoint or schedules a same-endpoint reconnect.
Common situations: A race where the connection's handoff state was reset between queuing and processing; a malformed MOVING that passed initial validation but carried no usable endpoint; concurrency between shutdown and handoff clearing the endpoint.
Related errors
- max handoff retries reached
- handoff queue is full, cannot queue new handoff requests - c
- circuit breaker is open, failing fast
- MaxWorkers must be greater than or equal to 0
- handoff queue size must be greater than 0
AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06).
Data as JSON: /data/errors/df103c839d54486f.json.
Report an issue: GitHub.