{"id":"79c8b81ad6561e4a","repo":"go-redis/redis","slug":"handoff-queue-is-full-cannot-queue-new-handoff-re","errorCode":null,"errorMessage":"handoff queue is full, cannot queue new handoff requests - consider increasing HandoffQueueSize or MaxWorkers in configuration","messagePattern":"handoff queue is full, cannot queue new handoff requests - consider increasing HandoffQueueSize or MaxWorkers in configuration","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"maintnotifications/errors.go","lineNumber":35,"sourceCode":"\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\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)","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/maintnotifications/errors.go#L17-L53","documentation":"Returned by queueHandoff (handoff_worker.go:326) when the handoff queue channel is full after waiting up to 100ms for a slot (handoff_worker.go:313-321). This means pending connection handoffs are accumulating faster than the worker pool can drain them; the offending connection is removed from the pool rather than queued (pool_hook.go:153-157).","triggerScenarios":"A burst of MOVING notifications (e.g. a large cluster rebalancing or multi-node failover) marks many connections for handoff simultaneously while MaxWorkers is low and/or HandoffQueueSize is undersized. OnPut tries to queue the handoff, the channel is full, ensureWorkerAvailable is already at maxWorkers, and the 100ms grace window expires.","commonSituations":"Small PoolSize driving a tiny auto-calculated queue; a migration storm; workers stuck on slow dials (HandoffTimeout long, endpoint unreachable) so they never free up to drain the queue.","solutions":["Increase HandoffQueueSize (explicit minimum 200) and/or MaxWorkers to match expected burst volume.","Raise PoolSize — both MaxWorkers and HandoffQueueSize auto-scale from it.","Investigate why workers are slow: unreachable new endpoints cause each worker to block for HandoffTimeout, starving the queue. Fix endpoint routing / EndpointType.","Monitor via GetCircuitBreakerStats and pool metrics; if the circuit breaker is opening, the upstream is unhealthy — resolve that root cause."],"exampleFix":"// before\ncfg := &maintnotifications.Config{MaxWorkers: 1, HandoffQueueSize: 5}\n\n// after\ncfg := maintnotifications.DefaultConfig()\ncfg.MaxWorkers = 20\ncfg.HandoffQueueSize = 1000","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if errors.Is(err, maintnotifications.ErrHandoffQueueFull) {\n    // backpressure: connection dropped, pool will use others. \n    // Retry the command; if persistent, raise HandoffQueueSize/MaxWorkers/PoolSize.\n}","preventionTips":["Size HandoffQueueSize and MaxWorkers for migration bursts, not steady state.","Scale PoolSize up — both auto-scale from it.","Fix unreachable endpoints so workers aren't blocked on HandoffTimeout.","Monitor circuit-breaker and queue stats."],"tags":["maintnotifications","handoff","queue","backpressure","runtime"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}