redis/go-redis · critical
redis: NewFailoverClusterClient nil options
Error message
redis: NewFailoverClusterClient nil options
What it means
NewFailoverClusterClient panics when the *FailoverOptions argument is nil. The constructor copies SentinelAddrs and builds a sentinelFailover from the options immediately, so a nil pointer is rejected up front with an explicit panic.
Source
Thrown at sentinel.go:1263
}
addr := net.JoinHostPort(parts[3], parts[4])
c.trySwitchMaster(pubsub.getContext(), addr)
}
if c.onUpdate != nil {
c.onUpdate(ctx)
}
}
}
//------------------------------------------------------------------------------
// NewFailoverClusterClient returns a client that supports routing read-only commands
// to a replica node.
// Passing nil FailoverOptions will cause a panic.
func NewFailoverClusterClient(failoverOpt *FailoverOptions) *ClusterClient {
if failoverOpt == nil {
panic("redis: NewFailoverClusterClient nil options")
}
sentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs))
copy(sentinelAddrs, failoverOpt.SentinelAddrs)
failover := &sentinelFailover{
opt: failoverOpt,
sentinelAddrs: sentinelAddrs,
}
opt := failoverOpt.clusterOptions()
if failoverOpt.DB != 0 {
onConnect := opt.OnConnect
opt.OnConnect = func(ctx context.Context, cn *Conn) error {
if err := cn.Select(ctx, failoverOpt.DB).Err(); err != nil {
return err
}View on GitHub (pinned to c5cad058c7)
Solutions
- Provide a populated &redis.FailoverOptions{MasterName: ..., SentinelAddrs: [...]}.
- Nil-check the options before calling NewFailoverClusterClient.
- If using NewUniversalClient, ensure opts.Failover() (populated UniversalOptions fields) yields non-nil FailoverOptions; leave MasterName empty if not using sentinel.
Example fix
// before
var opt *redis.FailoverOptions
client := redis.NewFailoverClusterClient(opt) // panics
// after
client := redis.NewFailoverClusterClient(&redis.FailoverOptions{
MasterName: "mymaster",
SentinelAddrs: []string{":26379"},
}) Defensive patterns
Strategy: validation
Validate before calling
func validateFailoverClusterOptions(opt *redis.FailoverOptions) error {
if opt == nil {
return errors.New("failover cluster options must not be nil")
}
if opt.MasterName == "" || len(opt.SentinelAddrs) == 0 {
return errors.New("MasterName and SentinelAddrs are required")
}
return nil
} Type guard
func hasFailoverClusterOptions(opt *redis.FailoverOptions) bool { return opt != nil } Try / catch
// Last-resort recover wrapper:
func safeNewFailoverClusterClient(opt *redis.FailoverOptions) (c *redis.ClusterClient, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("NewFailoverClusterClient: %v", r)
}
}()
return redis.NewFailoverClusterClient(opt), nil
} Prevention
- When using NewUniversalClient with MasterName set, make sure the Failover fields of UniversalOptions are populated so opts.Failover() is non-nil.
- Validate sentinel config in your config-loading layer before any client construction.
When it happens
Trigger: Calling redis.NewFailoverClusterClient(nil), or with an uninitialized *FailoverOptions. Also reachable via NewUniversalClient when opts.MasterName != "" and RouteByLatency/RouteRandomly/IsClusterMode are set, if UniversalOptions were hand-built with nil Failover fields and MasterName set.
Common situations: Config-driven client factories that select failover-cluster mode but skip options population; a nil FailoverOptions field inside UniversalOptions combined with a non-empty MasterName; test helpers that only partially populate options.
Related errors
- redis: NewFailoverClient nil options
- redis: NewSentinelClient nil options
- to route commands by latency, use NewFailoverClusterClient
- to route commands randomly, use NewFailoverClusterClient
- redis: NewUniversalClient nil options
AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01).
Data as JSON: /api/errors/fb3529c156a076d2.
Report an issue: GitHub.