{"record":{"id":"cfe0ee3f734a8e29","repo":"redis/go-redis","slug":"redis-newsentinelclient-nil-options","errorCode":null,"errorMessage":"redis: NewSentinelClient nil options","messagePattern":"redis: NewSentinelClient nil options","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"sentinel.go","lineNumber":684,"sourceCode":"\t\tif failover.opt.TLSConfig == nil {\n\t\t\treturn netDialer.DialContext(ctx, network, addr)\n\t\t}\n\t\treturn tls.DialWithDialer(netDialer, network, addr, failover.opt.TLSConfig)\n\t}\n}\n\n//------------------------------------------------------------------------------\n\n// SentinelClient is a client for a Redis Sentinel.\ntype SentinelClient struct {\n\t*baseClient\n}\n\n// NewSentinelClient returns a Redis Sentinel client.\n// Passing nil Options will cause a panic.\nfunc NewSentinelClient(opt *Options) *SentinelClient {\n\tif opt == nil {\n\t\tpanic(\"redis: NewSentinelClient nil options\")\n\t}\n\topt.init()\n\tc := &SentinelClient{\n\t\tbaseClient: &baseClient{\n\t\t\tapClosed: &atomic.Bool{},\n\t\t\topt:      opt,\n\t\t\tonClose:  &onCloseHooks{},\n\t\t},\n\t}\n\n\t// Initialize push notification processor using shared helper\n\t// Use void processor for Sentinel clients\n\tc.pushProcessor = NewVoidPushNotificationProcessor()\n\n\tc.initHooks(hooks{\n\t\tdial:    c.baseClient.dial,\n\t\tprocess: c.baseClient.process,\n\t})","sourceCodeStart":666,"sourceCodeEnd":702,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/sentinel.go#L666-L702","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvar opt *redis.Options\nsc := redis.NewSentinelClient(opt) // panics\n\n// after\nsc := redis.NewSentinelClient(&redis.Options{Addr: \"sentinel:26379\"})","handlingStrategy":"validation","validationCode":"func validateSentinelOptions(opt *redis.Options) error {\n    if opt == nil {\n        return errors.New(\"sentinel options must not be nil\")\n    }\n    if opt.Addr == \"\" {\n        return errors.New(\"Addr (sentinel host:port) is required\")\n    }\n    return nil\n}","typeGuard":"func hasSentinelOptions(opt *redis.Options) bool { return opt != nil }","tryCatchPattern":"// Panics are not catchable in Go; prefer pre-validation. Last-resort recover:\nfunc safeNewSentinelClient(opt *redis.Options) (sc *redis.SentinelClient, err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"NewSentinelClient: %v\", r)\n        }\n    }()\n    return redis.NewSentinelClient(opt), nil\n}","preventionTips":["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."],"tags":["panic","nil-options","sentinel","configuration"],"backgroundTag":"nil-options-panic","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}