go-redis/redis · critical

redis: NewClusterClient nil options

Error message

redis: NewClusterClient nil options

What it means

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.

Source

Thrown at osscluster.go:1193

	// himport is the cluster-wide HIMPORT fieldset registry, shared with
	// every node client (masters and replicas alike — roles change with the
	// topology) so any connection serving an HIMPORT SET can lazily replay
	// the PREPARE (see himport.go, himport_cluster.go).
	himport *himportRegistry

	autopipelinerMu     *sync.Mutex    // guards the autopipeliner fields against concurrent first-call creation
	autopipeliner       *AutoPipeliner // blocking face (ClusterClient.AutoPipeline)
	asyncAutopipeliner  *AutoPipeliner // deferred face (ClusterClient.AsyncAutoPipeline)
	autopipelinerClosed bool           // set by Close: refuse to resurrect a pipeliner on a closed client
}

// NewClusterClient returns a Redis Cluster client as described in
// https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec.
// Passing nil ClusterOptions will cause a panic.
func NewClusterClient(opt *ClusterOptions) *ClusterClient {
	if opt == nil {
		panic("redis: NewClusterClient nil options")
	}
	opt.init()

	c := &ClusterClient{
		opt:             opt,
		nodes:           newClusterNodes(opt),
		himport:         newHImportRegistry(),
		autopipelinerMu: &sync.Mutex{},
	}

	// Every node client shares the cluster-wide fieldset registry, replicas
	// included: a promoted replica's connections carry no prepared flags, so
	// the first HIMPORT SET routed to it replays the PREPARE lazily.
	c.nodes.OnNewNode(func(nodeClient *Client) {
		nodeClient.himport = c.himport
	})

	c.cmdsInfoCache = newCmdsInfoCache(c.cmdsInfo)

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Pass a non-nil *redis.ClusterOptions with at least Addrs populated.
  2. Handle config-load errors explicitly rather than propagating nil.
  3. Add a nil check at the call site returning a descriptive error.

Example fix

// before
var opt *redis.ClusterOptions
c := redis.NewClusterClient(opt)

// after
c := redis.NewClusterClient(&redis.ClusterOptions{
    Addrs: []string{":7000", ":7001", ":7002"},
})
Defensive patterns

Strategy: validation

Validate before calling

func buildClusterClient(opt *redis.ClusterOptions) (*redis.ClusterClient, error) {
    if opt == nil {
        return nil, errors.New("redis.ClusterOptions must not be nil")
    }
    return redis.NewClusterClient(opt), nil
}

Type guard

func isNonNilClusterOptions(opt *redis.ClusterOptions) bool { return opt != nil }

Prevention

When it happens

Trigger: Calling redis.NewClusterClient(nil), or passing a *redis.ClusterOptions variable that was never assigned (var opt *redis.ClusterOptions; NewClusterClient(opt)).

Common situations: Config-loader returning nil on error and the caller ignoring it, or a refactor dropping the options construction.

Related errors


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