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

  1. Pass a valid &redis.Options{Addr: "host:26379"} (the sentinel address) to NewSentinelClient.
  2. Guard the call site with a nil check on your options variable before constructing.
  3. 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

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


AI-assisted analysis of redis/go-redis@c5cad058c7 (2026-09-01). Data as JSON: /api/errors/cfe0ee3f734a8e29. Report an issue: GitHub.