go-redis/redis · critical

redis: failed to create pipeline connection pool: %w

Error message

redis: failed to create pipeline connection pool: %w

What it means

Panicked from NewClient when a separate pipeline connection pool is requested (PipelineReadBufferSize or PipelineWriteBufferSize > 0) and newConnPool(pipelineOpt, dialHook, name) fails. The pipeline pool has its own (cloned) options and default size of 10; failure is fatal.

Source

Thrown at redis.go:1987

			// parsing needs a minimum read buffer, and a tiny pipeline reader
			// would break push-notification handling on pipeline conns.
			if pipelineOpt.Protocol == 3 && pipelineOpt.ReadBufferSize < proto.MinRESP3ReadBufferSize {
				pipelineOpt.ReadBufferSize = proto.MinRESP3ReadBufferSize
			}
		}
		if opt.PipelineWriteBufferSize > 0 {
			pipelineOpt.WriteBufferSize = opt.PipelineWriteBufferSize
		}
		if opt.PipelinePoolSize > 0 {
			pipelineOpt.PoolSize = opt.PipelinePoolSize
		} else {
			pipelineOpt.PoolSize = 10 // default smaller pool for pipelining
		}
		pipelinePoolName := opt.Addr + "_" + uniqueID + "_pipeline"
		c.pipelinePoolName = pipelinePoolName
		c.pipelinePool, err = newConnPool(pipelineOpt, c.dialHook, pipelinePoolName)
		if err != nil {
			panic(fmt.Errorf("redis: failed to create pipeline connection pool: %w", err))
		}
	}

	if opt.StreamingCredentialsProvider != nil {
		c.streamingCredentialsManager = streaming.NewManager(c.connPool, c.opt.PoolTimeout)
		c.connPool.AddPoolHook(c.streamingCredentialsManager.PoolHook())
		if c.pipelinePool != nil {
			c.pipelinePool.AddPoolHook(c.streamingCredentialsManager.PoolHook())
		}
	}

	// CSC wiring (SharedTracking): shared cache + per-connection CLIENT TRACKING +
	// background drainer. attachCSC is the strategy dispatch entry.
	if opt.Protocol == 3 {
		var cache Cache
		if explicit := opt.ClientSideCache; explicit != nil {
			cache = explicit
		} else if cfg := opt.ClientSideCacheConfig; cfg != nil {

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Wrap NewClient in recover().
  2. If PipelinePoolSize is set, ensure it is > 0; if unset, the default of 10 is used.
  3. Validate PipelineReadBufferSize/PipelineWriteBufferSize are positive and reasonable (>= proto.MinRESP3ReadBufferSize when Protocol == 3).
  4. Temporarily remove the pipeline buffer options to confirm the pipeline pool is the failing component.

Example fix

// before
opt := &redis.Options{
    Addr: addr,
    PipelineReadBufferSize: 16,
    PipelinePoolSize:      0, // intentionally unset is fine; negative breaks
}

// after
opt := &redis.Options{
    Addr: addr,
    PipelineReadBufferSize: 64 * 1024,
}
Defensive patterns

Strategy: validation

Validate before calling

if (opt.PipelineReadBufferSize > 0 || opt.PipelineWriteBufferSize > 0) {
    if opt.PipelineReadBufferSize > 0 && opt.Protocol == 3 &&
        opt.PipelineReadBufferSize < proto.MinRESP3ReadBufferSize {
        return errors.New("PipelineReadBufferSize below RESP3 minimum")
    }
    if opt.PipelinePoolSize < 0 {
        return errors.New("PipelinePoolSize must be >= 0")
    }
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        return nil, fmt.Errorf("redis NewClient (pipeline pool) failed: %v", r)
    }
}()
return redis.NewClient(opt), nil

Prevention

When it happens

Trigger: Setting PipelineReadBufferSize/PipelineWriteBufferSize > 0 and the resulting pipelineOpt produces an invalid pool configuration (e.g. PipelinePoolSize <= 0 combined with the clone logic, or buffer size below the RESP3 minimum clamp).

Common situations: PipelinePoolSize set to 0 or negative; PipelineReadBufferSize set very small causing the RESP3 minimum clamp to interact unexpectedly; a hook or dialer shared with the main pool failing during the pipeline pool build.

Related errors


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