{"id":"d64974d819b18c51","repo":"go-redis/redis","slug":"redis-newring-nil-options","errorCode":null,"errorMessage":"redis: NewRing nil options","messagePattern":"redis: NewRing nil options","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"ring.go","lineNumber":628,"sourceCode":"//\n// Ring should be used when you need multiple Redis servers for caching\n// and can tolerate losing data when one of the servers dies.\n// Otherwise you should use Redis Cluster.\ntype Ring struct {\n\tcmdable\n\thooksMixin\n\n\topt               *RingOptions\n\tsharding          *ringSharding\n\tcmdsInfoCache     *cmdsInfoCache\n\theartbeatCancelFn context.CancelFunc\n}\n\n// NewRing returns a Redis Ring client to the Redis Server specified by RingOptions.\n// Passing nil RingOptions will cause a panic.\nfunc NewRing(opt *RingOptions) *Ring {\n\tif opt == nil {\n\t\tpanic(\"redis: NewRing nil options\")\n\t}\n\t// Shallow-copy the options: the ring-wide HIMPORT registry is carried\n\t// through them to shard construction, and reusing one caller-owned\n\t// RingOptions across several rings must not make the rings share (or\n\t// clobber each other's) registry.\n\toptCopy := *opt\n\topt = &optCopy\n\topt.init()\n\t// The registry must exist before the first shard is created; shards\n\t// adopt it in newRingShard.\n\topt.himport = newHImportRegistry()\n\n\thbCtx, hbCancel := context.WithCancel(context.Background())\n\n\tring := Ring{\n\t\topt:               opt,\n\t\tsharding:          newRingSharding(opt),\n\t\theartbeatCancelFn: hbCancel,","sourceCodeStart":610,"sourceCodeEnd":646,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/ring.go#L610-L646","documentation":"NewRing panics with 'redis: NewRing nil options' when the supplied *RingOptions is nil. Like NewClient, the constructor (ring.go:626-629) documents and enforces this with a panic because a Ring needs at least the Addrs map to build shards; a nil options struct is a programming defect. The panic happens during ring initialization, before any heartbeat or shard is started.","triggerScenarios":"Calling redis.NewRing(nil); passing a *RingOptions variable that was declared but never assigned; a config builder that returns nil RingOptions when no addrs were configured; refactoring that extracts ring construction into a helper returning nil on the empty case.","commonSituations":"Multi-shard config sourced from YAML/env where the ring section is missing so the loader yields nil; conditional client selection (Ring vs Client vs Cluster) where the Ring branch runs with an unpopulated options pointer; tests that omit the options argument.","solutions":["Always pass a non-nil *RingOptions, at minimum &redis.RingOptions{Addrs: map[string]string{...}}.","Make your options loader return a valid *RingOptions with sane defaults rather than nil; fail loudly in config validation if Addrs is empty.","Guard at the call site: if opt == nil { opt = &redis.RingOptions{Addrs: defaultAddrs} } or surface an error from your factory.","Add a constructor wrapper that validates Addrs is non-empty and returns an error instead of letting go-redis panic."],"exampleFix":"// before\nring := redis.NewRing(loadRingCfg()) // returns nil\n\n// after\nopt := loadRingCfg()\nif opt == nil {\n    opt = &redis.RingOptions{Addrs: map[string]string{\"shard1\": \":6379\"}}\n}\nring := redis.NewRing(opt)","handlingStrategy":"validation","validationCode":"func newRing(opt *redis.RingOptions) (*redis.Ring, error) {\n    if opt == nil {\n        return nil, errors.New(\"ring options must not be nil\")\n    }\n    if len(opt.Addrs) == 0 {\n        return nil, errors.New(\"ring options require at least one addr\")\n    }\n    return redis.NewRing(opt), nil\n}","typeGuard":"func validRingOptions(opt *redis.RingOptions) bool {\n    return opt != nil && len(opt.Addrs) > 0\n}","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        log.Fatalf(\"redis ring init failed: %v\", r)\n    }\n}()\nring := redis.NewRing(opt)","preventionTips":["Always pass &redis.RingOptions{Addrs: map[string]string{...}}.","Have your config loader return a valid *RingOptions with defaults instead of nil.","Wrap NewRing in a factory that validates Addrs and returns an error.","Validate config (non-empty Addrs) at startup, not on first command."],"tags":["ring","panic","configuration","constructor","nil-guard"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}