redis/go-redis · critical
redis: failed to create connection pool: %w
Error message
redis: failed to create connection pool: %w
What it means
NewFailoverClient dials nothing itself, but it does build the local connection pool. If pool construction (newConnPool) fails, the client panics, wrapping the underlying error with this message. Because pools allocate buffers/resources eagerly, failure here usually indicates invalid pool configuration rather than a network problem.
Source
Thrown at sentinel.go:593
onClose: &onCloseHooks{},
himport: newHImportRegistry(),
},
}
rdb.init()
// Initialize push notification processor using shared helper
// Use void processor by default for RESP2 connections
rdb.pushProcessor = initializePushProcessor(opt)
// Generate unique pool names for metrics
uniqueID := generateUniqueID()
mainPoolName := opt.Addr + "_" + uniqueID
pubsubPoolName := opt.Addr + "_" + uniqueID + "_pubsub"
var err error
rdb.connPool, err = newConnPool(opt, rdb.dialHook, mainPoolName)
if err != nil {
panic(fmt.Errorf("redis: failed to create connection pool: %w", err))
}
rdb.pubSubPool, err = newPubSubPool(opt, rdb.dialHook, pubsubPoolName)
if err != nil {
panic(fmt.Errorf("redis: failed to create pubsub pool: %w", err))
}
// Create the dedicated pipeline pool unconditionally, mirroring NewClient
// via the shared buildPipelinePool helper. PipelinePoolSize < 0 opts out.
if opt.PipelinePoolSize >= 0 {
ref, err := rdb.buildPipelinePool(mainPoolName + "_pipeline")
if err != nil {
panic(fmt.Errorf("redis: failed to create pipeline connection pool: %w", err))
}
rdb.pipelinePool = ref
}
// Register pools for OTel async gauge metrics, matching NewClient (the
// failover client previously registered none, so pool gauges were silentView on GitHub (pinned to c5cad058c7)
Solutions
- Recover the panic and inspect the wrapped error (%w) to find the root cause
- Validate FailoverOptions before construction (Dialer set, sane PoolSize/MinIdleConns)
- If constructing via URL, run ParseFailoverURL first so invalid values are rejected as errors instead of panics
Example fix
// before
rdb := redis.NewFailoverClient(opt) // panics on bad pool config
// after
if opt.Dialer == nil && opt.Addr == "" { return fmt.Errorf("missing dialer") }
rdb := redis.NewFailoverClient(opt) Defensive patterns
Strategy: try-catch
Validate before calling
if opt == nil { return errors.New("nil FailoverOptions") }
if opt.Dialer == nil && len(opt.SentinelAddrs) == 0 { return errors.New("no dialer or sentinel addrs") } Try / catch
func newFailoverSafe(opt *redis.FailoverOptions) (rdb *redis.Client, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("NewFailoverClient: %v", r)
}
}()
rdb = redis.NewFailoverClient(opt)
return rdb, nil
} Prevention
- Build options via ParseFailoverURL to get error returns instead of panics
- Validate pool-related options (PoolSize, MinIdleConns, Dialer) before construction
- Wrap client construction in a recover helper at app startup
When it happens
Trigger: Calling NewFailoverClient (or NewUniversalClient with sentinel options) with options that make newConnPool fail — e.g. an invalid Dialer, nil/broken Dialer with custom settings, or hook/pool configuration that errors at setup.
Common situations: Programmatic construction of FailoverOptions with fields that bypass URL validation, misconfigured custom dialers in tests, or options produced by merging configs where the pool setup becomes invalid.
Related errors
- redis: failed to create pubsub pool: %w
- redis: failed to create connection pool: %w
- redis: invalid URL scheme: %s
- redis: invalid database number: %q
- redis: invalid URL path: %s
AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01).
Data as JSON: /api/errors/f3b49aa61b616468.
Report an issue: GitHub.