redis/go-redis · critical
redis: failed to create pubsub pool: %w
Error message
redis: failed to create pubsub pool: %w
What it means
NewFailoverClient also creates a dedicated Pub/Sub connection pool; failure in newPubSubPool causes a panic wrapping the underlying error with this message. Like the main pool error, this points at invalid pool options rather than a live network failure, since pool construction is local setup.
Source
Thrown at sentinel.go:597
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 silent
// for the identical standalone setup). The pipeline pool is nil when not
// configured.
otel.RegisterPools(rdb.connPool, rdb.pubSubPool, rdb.getPipelinePool(), opt.Addr)
View on GitHub (pinned to c5cad058c7)
Solutions
- Recover and inspect the wrapped error for the root cause
- Verify the same FailoverOptions work with a plain NewClient to isolate sentinel-specific handling
- Fix the offending option (Dialer, buffer sizes, PoolSize) before constructing the client
Example fix
// before
rdb := redis.NewFailoverClient(opt) // panics: failed to create pubsub pool
// after
if opt.ReadBufferSize <= 0 { opt.ReadBufferSize = 32 * 1024 }
rdb := redis.NewFailoverClient(opt) Defensive patterns
Strategy: try-catch
Validate before calling
if opt == nil { return errors.New("nil FailoverOptions") }
if opt.PoolSize < 0 { return fmt.Errorf("invalid PoolSize %d", opt.PoolSize) } 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
- Don't reuse a mutable FailoverOptions struct across clients after mutation
- Keep buffer-size and pool options at library defaults unless profiling says otherwise
- Wrap construction in a recover helper and log the wrapped cause
When it happens
Trigger: NewFailoverClient / NewUniversalClient with sentinel options where newPubSubPool fails — typically the same invalid dialer or pool-option conditions as the main-pool failure, but surfacing in the pubsub pool step.
Common situations: Shared options struct reused across clients after one field was mutated to an invalid value, custom hooks that break pool setup, or memory/resource constraints during eager buffer allocation.
Related errors
- redis: failed to create connection pool: %w
- redis: failed to create connection pool: %w
- redis: failed to create pubsub pool: %w
- redis: invalid URL scheme: %s
- redis: invalid database number: %q
AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01).
Data as JSON: /api/errors/002d74844abb7045.
Report an issue: GitHub.