go-redis/redis · error

redis: connection pool exhausted

Error message

redis: connection pool exhausted

What it means

pool.ErrPoolExhausted is returned when the number of active connections has reached MaxActiveConns and a new connection is requested without waiting (pool.go:60-62, thrown at pool.go:627/649). Unlike ErrPoolTimeout this fails immediately (no queue wait) on the NewConn path when MaxActiveConns is bounded.

Source

Thrown at internal/pool/pool.go:62

// These represent the logical state of a connection from a metrics perspective,
// not the internal state machine state (ConnState).
const (
	// MetricStateIdle indicates the connection is idle in the pool,
	// ready to be acquired.
	MetricStateIdle = "idle"

	// MetricStateUsed indicates the connection is currently being used
	// by a client operation.
	MetricStateUsed = "used"
)

var (
	// ErrClosed performs any operation on the closed client will return this error.
	ErrClosed = errors.New("redis: client is closed")

	// ErrPoolExhausted is returned from a pool connection method
	// when the maximum number of database connections in the pool has been reached.
	ErrPoolExhausted = errors.New("redis: connection pool exhausted")

	// ErrPoolTimeout timed out waiting to get a connection from the connection pool.
	ErrPoolTimeout = errors.New("redis: connection pool timeout")

	// ErrConnUnusableTimeout is returned when a connection is not usable and we timed out trying to mark it as unusable.
	ErrConnUnusableTimeout = errors.New("redis: timed out trying to mark connection as unusable")

	// errHookRequestedRemoval is returned when a hook requests connection removal.
	errHookRequestedRemoval = errors.New("hook requested removal")

	// errConnNotPooled is returned when trying to return a non-pooled connection to the pool.
	errConnNotPooled = errors.New("connection not pooled")

	// errConnEvictedIdle is passed to OnRemove hooks when a pooled connection is evicted on
	// Put because the idle pool is already at MaxIdleConns.
	errConnEvictedIdle = errors.New("connection evicted: idle pool at capacity")

	// metricCallbackMu protects all global metric callback functions for thread-safe access.

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Increase Options.PoolSize (and MinIdleConns) to match peak concurrency.
  2. Eliminate connection leaks — ensure transactions/pipelines complete and return conns.
  3. Reduce concurrency or shorten long-running blocking commands.
  4. Set MaxActiveConns appropriately or leave 0 to tie it to PoolSize.

Example fix

// before
opt := &redis.Options{Addr: addr} // small default pool
// after
opt := &redis.Options{
    Addr:         addr,
    PoolSize:     64,
    MinIdleConns: 16,
}
Defensive patterns

Strategy: validation

Validate before calling

// Size the pool for peak concurrency before load.
opts := &redis.Options{Addr: addr, PoolSize: runtime.GOMAXPROCS(0) * 16, MinIdleConns: 16}

Try / catch

err := client.Get(ctx, key).Err()
if errors.Is(err, redis.ErrPoolExhausted) {
    // back off briefly and retry; or report load shedding
    time.Sleep(backoff)
}

Prevention

When it happens

Trigger: More concurrent operations than MaxActiveConns, or a spike in demand while existing connections are checked out; also triggered when PoolSize/MaxActiveConns is misconfigured for the workload.

Common situations: Default PoolSize (10*runtime.NumCPU) too small for high concurrency, long-running commands (BLPOP, long scripts) hoarding connections, or connection leaks (conn not Put back).

Related errors


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