{"id":"0ce17bca4ce4a552","repo":"go-redis/redis","slug":"redis-newfailoverclusterclient-nil-options","errorCode":null,"errorMessage":"redis: NewFailoverClusterClient nil options","messagePattern":"redis: NewFailoverClusterClient nil options","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"sentinel.go","lineNumber":1270,"sourceCode":"\t\t\t}\n\t\t\taddr := net.JoinHostPort(parts[3], parts[4])\n\t\t\tc.trySwitchMaster(pubsub.getContext(), addr)\n\t\t}\n\n\t\tif c.onUpdate != nil {\n\t\t\tc.onUpdate(ctx)\n\t\t}\n\t}\n}\n\n//------------------------------------------------------------------------------\n\n// NewFailoverClusterClient returns a client that supports routing read-only commands\n// to a replica node.\n// Passing nil FailoverOptions will cause a panic.\nfunc NewFailoverClusterClient(failoverOpt *FailoverOptions) *ClusterClient {\n\tif failoverOpt == nil {\n\t\tpanic(\"redis: NewFailoverClusterClient nil options\")\n\t}\n\n\tsentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs))\n\tcopy(sentinelAddrs, failoverOpt.SentinelAddrs)\n\n\tfailover := &sentinelFailover{\n\t\topt:           failoverOpt,\n\t\tsentinelAddrs: sentinelAddrs,\n\t}\n\n\topt := failoverOpt.clusterOptions()\n\tif failoverOpt.DB != 0 {\n\t\tonConnect := opt.OnConnect\n\n\t\topt.OnConnect = func(ctx context.Context, cn *Conn) error {\n\t\t\tif err := cn.Select(ctx, failoverOpt.DB).Err(); err != nil {\n\t\t\t\treturn err\n\t\t\t}","sourceCodeStart":1252,"sourceCodeEnd":1288,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/sentinel.go#L1252-L1288","documentation":"NewFailoverClusterClient panics when failoverOpt is nil. Like NewSentinelClient, the function immediately dereferences failoverOpt (reads SentinelAddrs), so the guard converts a latent nil-deref into a clear, explicit panic.","triggerScenarios":"Calling redis.NewFailoverClusterClient(nil), or passing a *redis.FailoverOptions that was conditionally constructed and left nil.","commonSituations":"Config-builder function returning nil on a parse error that the caller ignored, or a refactor that removed the options assignment.","solutions":["Construct and pass a non-nil *redis.FailoverOptions with MasterName and SentinelAddrs.","Handle config-load errors explicitly instead of letting a nil pointer reach the constructor.","Add a guard at the call site that returns a descriptive error when options are missing."],"exampleFix":"// before\nvar fo *redis.FailoverOptions\nc := redis.NewFailoverClusterClient(fo)\n\n// after\nc := redis.NewFailoverClusterClient(&redis.FailoverOptions{\n    MasterName:   \"mymaster\",\n    SentinelAddrs: []string{\"sentinel:26379\"},\n})","handlingStrategy":"validation","validationCode":"func buildFailoverCluster(fo *redis.FailoverOptions) (*redis.ClusterClient, error) {\n    if fo == nil {\n        return nil, errors.New(\"redis.FailoverOptions must not be nil\")\n    }\n    return redis.NewFailoverClusterClient(fo), nil\n}","typeGuard":"func isNonNilFailoverOptions(fo *redis.FailoverOptions) bool { return fo != nil }","tryCatchPattern":null,"preventionTips":["Always construct *redis.FailoverOptions with &redis.FailoverOptions{...} and set MasterName + SentinelAddrs.","Return (Options, error) from config loaders so nil never propagates.","Add a nil guard at the call site returning a descriptive error."],"tags":["sentinel","cluster","panic","configuration","nil-options"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}