go-redis/redis · critical

redis: NewUniversalClient nil options

Error message

redis: NewUniversalClient nil options

What it means

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.

Source

Thrown at universal.go:423

	// delegate to the underlying client.
	_ UniversalClient = (*AutoPipeliner)(nil)
)

// NewUniversalClient returns a new multi client. The type of the returned client depends
// on the following conditions:
//
//  1. If the MasterName option is specified with RouteByLatency, RouteRandomly or IsClusterMode,
//     a FailoverClusterClient is returned.
//  2. If the MasterName option is specified without RouteByLatency, RouteRandomly or IsClusterMode,
//     a sentinel-backed FailoverClient is returned.
//  3. If the number of Addrs is two or more, or IsClusterMode option is specified,
//     a ClusterClient is returned.
//  4. Otherwise, a single-node Client is returned.
//
// Passing nil UniversalOptions will cause a panic.
func NewUniversalClient(opts *UniversalOptions) UniversalClient {
	if opts == nil {
		panic("redis: NewUniversalClient nil options")
	}

	switch {
	case opts.MasterName != "" && (opts.RouteByLatency || opts.RouteRandomly || opts.IsClusterMode):
		return NewFailoverClusterClient(opts.Failover())
	case opts.MasterName != "":
		return NewFailoverClient(opts.Failover())
	case len(opts.Addrs) > 1 || opts.IsClusterMode:
		return NewClusterClient(opts.Cluster())
	default:
		return NewClient(opts.Simple())
	}
}

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Always pass a non-nil *redis.UniversalOptions, at minimum &redis.UniversalOptions{Addrs: []string{"localhost:6379"}}.
  2. 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.
  3. Guard the call site: if opts == nil { opts = &redis.UniversalOptions{Addrs: defaultAddrs} } or return an error from your factory.
  4. Wrap NewUniversalClient in a constructor that validates inputs and returns an error instead of letting the library panic.

Example fix

// before
client := redis.NewUniversalClient(loadUniversalCfg()) // returns nil

// after
opts := loadUniversalCfg()
if opts == nil {
    opts = &redis.UniversalOptions{Addrs: []string{"localhost:6379"}}
}
client := redis.NewUniversalClient(opts)
Defensive patterns

Strategy: validation

Validate before calling

func newUniversalClient(opts *redis.UniversalOptions) (redis.UniversalClient, error) {
    if opts == nil {
        return nil, errors.New("universal options must not be nil")
    }
    if len(opts.Addrs) == 0 && opts.MasterName == "" {
        return nil, errors.New("universal options require Addrs or MasterName")
    }
    return redis.NewUniversalClient(opts), nil
}

Type guard

func validUniversalOptions(opts *redis.UniversalOptions) bool {
    return opts != nil && (len(opts.Addrs) > 0 || opts.MasterName != "")
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        log.Fatalf("universal client init failed: %v", r)
    }
}()
client := redis.NewUniversalClient(opts)

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/1fd82987226a4782.json. Report an issue: GitHub.