redis/go-redis · critical

redis: failed to create connection pool: %w

Error message

redis: failed to create connection pool: %w

What it means

This panic wraps the error returned when go-redis fails to construct the main connection pool for a new Client. Pool construction itself almost never fails at creation time (it does not dial yet); a failure here indicates a programmatically invalid pool configuration. Because NewClient cannot return a partially usable client, the library panics.

Source

Thrown at redis.go:2116

	}
	c.init()

	// Initialize push notification processor using shared helper
	// Use void processor for RESP2 connections (push notifications not available)
	c.pushProcessor = initializePushProcessor(opt)
	// set opt push processor for child clients
	c.opt.PushNotificationProcessor = c.pushProcessor

	// Generate unique pool names for metrics
	uniqueID := generateUniqueID()
	mainPoolName := opt.Addr + "_" + uniqueID
	pubsubPoolName := opt.Addr + "_" + uniqueID + "_pubsub"

	// Create connection pools
	var err error
	c.connPool, err = newConnPool(opt, c.dialHook, mainPoolName)
	if err != nil {
		panic(fmt.Errorf("redis: failed to create connection pool: %w", err))
	}
	c.pubSubPool, err = newPubSubPool(opt, c.dialHook, pubsubPoolName)
	if err != nil {
		panic(fmt.Errorf("redis: failed to create pubsub pool: %w", err))
	}

	// Create the dedicated pipeline pool unconditionally, like pubSubPool: it
	// is pure burst capacity (no pre-dialing, small cap, larger buffers — see
	// pipelinePoolOptions), so an unused pipeline pool holds zero connections
	// and costs nothing. Pipelines stop competing with regular commands for
	// main-pool connections; a burst wider than the pool's cap spills back to
	// the main pool (see withPipelineConn). PipelinePoolSize < 0 opts out.
	if opt.PipelinePoolSize >= 0 {
		ref, err := c.buildPipelinePool(opt.Addr + "_" + uniqueID + "_pipeline")
		if err != nil {
			panic(fmt.Errorf("redis: failed to create pipeline connection pool: %w", err))
		}
		c.pipelinePool = ref

View on GitHub (pinned to c5cad058c7)

Solutions

  1. Print the wrapped cause with errors.Unwrap / %w — the inner error names the exact invalid option
  2. Fix the offending redis.Options field (e.g. ensure PoolSize > 0)
  3. Recover from the panic in wrappers that construct clients from user-supplied config and return the error instead

Example fix

// before
opt := &redis.Options{Addr: addr, PoolSize: cfg.PoolSize} // cfg.PoolSize = -1
client := redis.NewClient(opt) // panics
// after
poolSize := cfg.PoolSize
if poolSize <= 0 {
    poolSize = 10 * runtime.GOMAXPROCS(0)
}
opt := &redis.Options{Addr: addr, PoolSize: poolSize}
client := redis.NewClient(opt)
Defensive patterns

Strategy: validation

Validate before calling

if opt.PoolSize <= 0 { return fmt.Errorf("PoolSize must be positive, got %d", opt.PoolSize) }
_ = redis.NewClient(opt)

Try / catch

defer func() {
    if r := recover(); r != nil {
        err = fmt.Errorf("redis client init failed: %v", r)
    }
}()

Prevention

When it happens

Trigger: Calling redis.NewClient (or any client constructor that calls newConnPool at redis.go:2116) with options that make internal/pool.NewConnPool return an error — most commonly invalid buffer-size or pool-size combinations such as negative PoolSize, or a custom Dialer/option set rejected during pool init.

Common situations: Copy-pasted option structs with PoolSize set to a negative value; building Options dynamically from env vars where a missing numeric default becomes a negative; mixing PoolSize/MinIdleConns constraints that the pool constructor rejects.

Related errors


AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01). Data as JSON: /api/errors/77a4863730967b94. Report an issue: GitHub.