{"id":"1fd82987226a4782","repo":"go-redis/redis","slug":"redis-newuniversalclient-nil-options","errorCode":null,"errorMessage":"redis: NewUniversalClient nil options","messagePattern":"redis: NewUniversalClient nil options","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"universal.go","lineNumber":423,"sourceCode":"\t// delegate to the underlying client.\n\t_ UniversalClient = (*AutoPipeliner)(nil)\n)\n\n// NewUniversalClient returns a new multi client. The type of the returned client depends\n// on the following conditions:\n//\n//  1. If the MasterName option is specified with RouteByLatency, RouteRandomly or IsClusterMode,\n//     a FailoverClusterClient is returned.\n//  2. If the MasterName option is specified without RouteByLatency, RouteRandomly or IsClusterMode,\n//     a sentinel-backed FailoverClient is returned.\n//  3. If the number of Addrs is two or more, or IsClusterMode option is specified,\n//     a ClusterClient is returned.\n//  4. Otherwise, a single-node Client is returned.\n//\n// Passing nil UniversalOptions will cause a panic.\nfunc NewUniversalClient(opts *UniversalOptions) UniversalClient {\n\tif opts == nil {\n\t\tpanic(\"redis: NewUniversalClient nil options\")\n\t}\n\n\tswitch {\n\tcase opts.MasterName != \"\" && (opts.RouteByLatency || opts.RouteRandomly || opts.IsClusterMode):\n\t\treturn NewFailoverClusterClient(opts.Failover())\n\tcase opts.MasterName != \"\":\n\t\treturn NewFailoverClient(opts.Failover())\n\tcase len(opts.Addrs) > 1 || opts.IsClusterMode:\n\t\treturn NewClusterClient(opts.Cluster())\n\tdefault:\n\t\treturn NewClient(opts.Simple())\n\t}\n}\n","sourceCodeStart":405,"sourceCodeEnd":437,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/universal.go#L405-L437","documentation":"NewUniversalClient panics with 'redis: NewUniversalClient nil options' when the supplied *UniversalOptions is nil (universal.go:421-424). The universal client branches on fields of opts (MasterName, Addrs, IsClusterMode, etc.) to decide whether to build a Failover, Cluster, Ring, or simple Client; a nil pointer has no fields to inspect, so the constructor documents and enforces non-nil with a panic at startup.","triggerScenarios":"Calling redis.NewUniversalClient(nil); passing a *UniversalOptions variable that was declared but never assigned; a config loader that returns nil UniversalOptions on an unhandled branch; conditional client setup that leaves opts nil in some path.","commonSituations":"Universal config sourced from env/YAML where no fields were set so the builder returns nil; client-type selection logic that reaches NewUniversalClient with an unpopulated options pointer; refactors that extract construction behind a helper returning nil on the empty case; tests that omit the options argument.","solutions":["Always pass a non-nil *redis.UniversalOptions, at minimum &redis.UniversalOptions{Addrs: []string{\"localhost:6379\"}}.","Make your options loader return a valid *UniversalOptions with defaults rather than nil; validate config and fail with a clear error if Addrs is empty.","Guard the call site: if opts == nil { opts = &redis.UniversalOptions{Addrs: defaultAddrs} } or return an error from your factory.","Wrap NewUniversalClient in a constructor that validates inputs and returns an error instead of letting the library panic."],"exampleFix":"// before\nclient := redis.NewUniversalClient(loadUniversalCfg()) // returns nil\n\n// after\nopts := loadUniversalCfg()\nif opts == nil {\n    opts = &redis.UniversalOptions{Addrs: []string{\"localhost:6379\"}}\n}\nclient := redis.NewUniversalClient(opts)","handlingStrategy":"validation","validationCode":"func newUniversalClient(opts *redis.UniversalOptions) (redis.UniversalClient, error) {\n    if opts == nil {\n        return nil, errors.New(\"universal options must not be nil\")\n    }\n    if len(opts.Addrs) == 0 && opts.MasterName == \"\" {\n        return nil, errors.New(\"universal options require Addrs or MasterName\")\n    }\n    return redis.NewUniversalClient(opts), nil\n}","typeGuard":"func validUniversalOptions(opts *redis.UniversalOptions) bool {\n    return opts != nil && (len(opts.Addrs) > 0 || opts.MasterName != \"\")\n}","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        log.Fatalf(\"universal client init failed: %v\", r)\n    }\n}()\nclient := redis.NewUniversalClient(opts)","preventionTips":["Always pass &redis.UniversalOptions{Addrs: []string{...}} or set MasterName for sentinel.","Have your config loader return a non-nil *UniversalOptions with defaults.","Wrap NewUniversalClient in a factory that validates Addrs/MasterName and returns an error.","Unit-test the config builder to assert non-nil and non-empty routing fields."],"tags":["universal","panic","configuration","constructor","nil-guard"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}