{"record":{"id":"1cfcd90d79ef08f8","repo":"redis/go-redis","slug":"redis-newfailoverclient-nil-options","errorCode":null,"errorMessage":"redis: NewFailoverClient nil options","messagePattern":"redis: NewFailoverClient nil options","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"sentinel.go","lineNumber":545,"sourceCode":"\tif o.TLSConfig != nil && q.has(\"skip_verify\") {\n\t\to.TLSConfig.InsecureSkipVerify = q.bool(\"skip_verify\")\n\t}\n\n\t// any parameters left?\n\tif r := q.remaining(); len(r) > 0 {\n\t\treturn nil, fmt.Errorf(\"redis: unexpected option: %s\", strings.Join(r, \", \"))\n\t}\n\n\treturn o, nil\n}\n\n// NewFailoverClient returns a Redis client that uses Redis Sentinel\n// for automatic failover. It's safe for concurrent use by multiple\n// goroutines.\n// Passing nil FailoverOptions will cause a panic.\nfunc NewFailoverClient(failoverOpt *FailoverOptions) *Client {\n\tif failoverOpt == nil {\n\t\tpanic(\"redis: NewFailoverClient nil options\")\n\t}\n\n\tif failoverOpt.RouteByLatency {\n\t\tpanic(\"to route commands by latency, use NewFailoverClusterClient\")\n\t}\n\tif failoverOpt.RouteRandomly {\n\t\tpanic(\"to route commands randomly, use NewFailoverClusterClient\")\n\t}\n\n\tsentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs))\n\tcopy(sentinelAddrs, failoverOpt.SentinelAddrs)\n\n\trand.Shuffle(len(sentinelAddrs), func(i, j int) {\n\t\tsentinelAddrs[i], sentinelAddrs[j] = sentinelAddrs[j], sentinelAddrs[i]\n\t})\n\n\tfailover := &sentinelFailover{\n\t\topt:           failoverOpt,","sourceCodeStart":527,"sourceCodeEnd":563,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/sentinel.go#L527-L563","documentation":"NewFailoverClient panics immediately when the *FailoverOptions argument is nil. The constructor requires a non-nil options struct because it reads SentinelAddrs, MasterName and other fields directly to build the client. The library deliberately fails fast instead of returning a half-configured client.","triggerScenarios":"Calling redis.NewFailoverClient(nil), or calling it with a *FailoverOptions variable that was declared but never assigned (nil pointer).","commonSituations":"Building options conditionally in a helper that returns *FailoverOptions and returning nil on a config-parse failure; loading options from env/flags where the failover section is missing; passing a nil pointer through a factory or NewUniversalClient path that forwards opts.Failover() results.","solutions":["Construct a valid &redis.FailoverOptions{...} with at least MasterName and SentinelAddrs set before calling NewFailoverClient.","Add a nil check (or a config loader that never returns nil) before invoking NewFailoverClient.","If sentinel failover is not actually needed, use redis.NewClient(&redis.Options{...}) instead."],"exampleFix":"// before\nvar opt *redis.FailoverOptions\nclient := redis.NewFailoverClient(opt) // panics\n\n// after\nopt := &redis.FailoverOptions{\n    MasterName:    \"mymaster\",\n    SentinelAddrs: []string{\":26379\", \":26380\"},\n}\nclient := redis.NewFailoverClient(opt)","handlingStrategy":"validation","validationCode":"func validateFailoverOptions(opt *redis.FailoverOptions) error {\n    if opt == nil {\n        return errors.New(\"failover options must not be nil\")\n    }\n    if opt.MasterName == \"\" || len(opt.SentinelAddrs) == 0 {\n        return errors.New(\"MasterName and SentinelAddrs are required\")\n    }\n    if opt.RouteByLatency || opt.RouteRandomly {\n        return errors.New(\"use NewFailoverClusterClient for routing options\")\n    }\n    return nil\n}","typeGuard":"func hasFailoverOptions(opt *redis.FailoverOptions) bool { return opt != nil }","tryCatchPattern":"// Go panics are not catchable via try/catch; recover only as a last resort:\nfunc safeNewFailoverClient(opt *redis.FailoverOptions) (c *redis.Client, err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"NewFailoverClient: %v\", r)\n        }\n    }()\n    return redis.NewFailoverClient(opt), nil\n}","preventionTips":["Always build FailoverOptions as a struct literal at the call site rather than reusing a possibly-nil pointer.","Centralize client construction in one factory that validates options first.","Load sentinel settings from config with explicit 'missing' errors instead of returning nil."],"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"}