redis/go-redis · critical
redis: NewSentinelClient nil options
Error message
redis: NewSentinelClient nil options
What it means
NewSentinelClient panics when the *Options argument is nil. It immediately dereferences opt (opt.init(), opt.Addr, etc.) to build the SentinelClient, so a nil pointer would crash later; the library panics with a clear message instead.
Source
Thrown at sentinel.go:684
if failover.opt.TLSConfig == nil {
return netDialer.DialContext(ctx, network, addr)
}
return tls.DialWithDialer(netDialer, network, addr, failover.opt.TLSConfig)
}
}
//------------------------------------------------------------------------------
// SentinelClient is a client for a Redis Sentinel.
type SentinelClient struct {
*baseClient
}
// NewSentinelClient returns a Redis Sentinel client.
// Passing nil Options will cause a panic.
func NewSentinelClient(opt *Options) *SentinelClient {
if opt == nil {
panic("redis: NewSentinelClient nil options")
}
opt.init()
c := &SentinelClient{
baseClient: &baseClient{
apClosed: &atomic.Bool{},
opt: opt,
onClose: &onCloseHooks{},
},
}
// Initialize push notification processor using shared helper
// Use void processor for Sentinel clients
c.pushProcessor = NewVoidPushNotificationProcessor()
c.initHooks(hooks{
dial: c.baseClient.dial,
process: c.baseClient.process,
})View on GitHub (pinned to c5cad058c7)
Solutions
- Pass a valid &redis.Options{Addr: "host:26379"} (the sentinel address) to NewSentinelClient.
- Guard the call site with a nil check on your options variable before constructing.
- If you meant to talk to a Redis server via sentinel, use NewFailoverClient instead.
Example fix
// before
var opt *redis.Options
sc := redis.NewSentinelClient(opt) // panics
// after
sc := redis.NewSentinelClient(&redis.Options{Addr: "sentinel:26379"}) Defensive patterns
Strategy: validation
Validate before calling
func validateSentinelOptions(opt *redis.Options) error {
if opt == nil {
return errors.New("sentinel options must not be nil")
}
if opt.Addr == "" {
return errors.New("Addr (sentinel host:port) is required")
}
return nil
} Type guard
func hasSentinelOptions(opt *redis.Options) bool { return opt != nil } Try / catch
// Panics are not catchable in Go; prefer pre-validation. Last-resort recover:
func safeNewSentinelClient(opt *redis.Options) (sc *redis.SentinelClient, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("NewSentinelClient: %v", r)
}
}()
return redis.NewSentinelClient(opt), nil
} Prevention
- Rarely construct SentinelClient directly — prefer NewFailoverClient for sentinel-backed HA.
- Build Options with a literal including Addr pointing at the sentinel, not the Redis server port.
When it happens
Trigger: Calling redis.NewSentinelClient(nil), or passing a *Options variable that was never initialized. Called internally by replicaAddrs/masterAddr paths of the failover client, but those always build options from validated FailoverOptions.
Common situations: Directly constructing a SentinelClient (uncommon) with options assembled conditionally; a helper returning nil *Options on config error; refactoring that removed the options-literal but left the call.
Related errors
- redis: NewFailoverClient nil options
- redis: NewFailoverClusterClient nil options
- redis: NewUniversalClient nil options
- redis: failed to create connection pool: %w
- redis: failed to create pubsub pool: %w
AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01).
Data as JSON: /api/errors/cfe0ee3f734a8e29.
Report an issue: GitHub.