{"id":"abd93580a7c68007","repo":"go-redis/redis","slug":"redis-newclusterclient-nil-options","errorCode":null,"errorMessage":"redis: NewClusterClient nil options","messagePattern":"redis: NewClusterClient nil options","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"osscluster.go","lineNumber":1193,"sourceCode":"\n\t// himport is the cluster-wide HIMPORT fieldset registry, shared with\n\t// every node client (masters and replicas alike — roles change with the\n\t// topology) so any connection serving an HIMPORT SET can lazily replay\n\t// the PREPARE (see himport.go, himport_cluster.go).\n\thimport *himportRegistry\n\n\tautopipelinerMu     *sync.Mutex    // guards the autopipeliner fields against concurrent first-call creation\n\tautopipeliner       *AutoPipeliner // blocking face (ClusterClient.AutoPipeline)\n\tasyncAutopipeliner  *AutoPipeliner // deferred face (ClusterClient.AsyncAutoPipeline)\n\tautopipelinerClosed bool           // set by Close: refuse to resurrect a pipeliner on a closed client\n}\n\n// NewClusterClient returns a Redis Cluster client as described in\n// https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec.\n// Passing nil ClusterOptions will cause a panic.\nfunc NewClusterClient(opt *ClusterOptions) *ClusterClient {\n\tif opt == nil {\n\t\tpanic(\"redis: NewClusterClient nil options\")\n\t}\n\topt.init()\n\n\tc := &ClusterClient{\n\t\topt:             opt,\n\t\tnodes:           newClusterNodes(opt),\n\t\thimport:         newHImportRegistry(),\n\t\tautopipelinerMu: &sync.Mutex{},\n\t}\n\n\t// Every node client shares the cluster-wide fieldset registry, replicas\n\t// included: a promoted replica's connections carry no prepared flags, so\n\t// the first HIMPORT SET routed to it replays the PREPARE lazily.\n\tc.nodes.OnNewNode(func(nodeClient *Client) {\n\t\tnodeClient.himport = c.himport\n\t})\n\n\tc.cmdsInfoCache = newCmdsInfoCache(c.cmdsInfo)","sourceCodeStart":1175,"sourceCodeEnd":1211,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/osscluster.go#L1175-L1211","documentation":"NewClusterClient panics when opt is nil. The constructor immediately calls opt.init() and reads opt fields, so the guard converts a guaranteed nil-pointer dereference into an explicit, descriptive panic.","triggerScenarios":"Calling redis.NewClusterClient(nil), or passing a *redis.ClusterOptions variable that was never assigned (var opt *redis.ClusterOptions; NewClusterClient(opt)).","commonSituations":"Config-loader returning nil on error and the caller ignoring it, or a refactor dropping the options construction.","solutions":["Pass a non-nil *redis.ClusterOptions with at least Addrs populated.","Handle config-load errors explicitly rather than propagating nil.","Add a nil check at the call site returning a descriptive error."],"exampleFix":"// before\nvar opt *redis.ClusterOptions\nc := redis.NewClusterClient(opt)\n\n// after\nc := redis.NewClusterClient(&redis.ClusterOptions{\n    Addrs: []string{\":7000\", \":7001\", \":7002\"},\n})","handlingStrategy":"validation","validationCode":"func buildClusterClient(opt *redis.ClusterOptions) (*redis.ClusterClient, error) {\n    if opt == nil {\n        return nil, errors.New(\"redis.ClusterOptions must not be nil\")\n    }\n    return redis.NewClusterClient(opt), nil\n}","typeGuard":"func isNonNilClusterOptions(opt *redis.ClusterOptions) bool { return opt != nil }","tryCatchPattern":null,"preventionTips":["Always construct *redis.ClusterOptions with &redis.ClusterOptions{Addrs: ...} inline.","Return (ClusterOptions, error) from config loaders to avoid nil propagation.","Add a nil guard at the call site."],"tags":["cluster","panic","configuration","nil-options"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}