go-redis/redis · info

connection marked for handoff

Error message

connection marked for handoff

What it means

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.

Source

Thrown at maintnotifications/errors.go:48

)

// Handoff errors
var (
	// ErrHandoffQueueFull is returned when the handoff queue is full
	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)
)

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Increase PoolSize so the pool has enough usable connections during handoff churn.
  2. Confirm MaxHandoffRetries/HandoffTimeout aren't leaving connections stuck unusable for long.
  3. If your pool wrapper surfaces OnGet errors directly to callers, translate this sentinel into a retry-on-another-connection rather than a hard failure.
  4. Check for an unhealthy upstream causing repeated re-auth or handoff (circuit breaker stats).
Defensive patterns

Strategy: retry

Try / catch

// This is normally handled inside the pool (it picks another conn).
// If surfacing from a custom hook wrapper, retry the command:
if errors.Is(err, maintnotifications.ErrConnectionMarkedForHandoff) {
    return retryCommand(ctx, cmd)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/5e4cb47460c0148e.json. Report an issue: GitHub.