{"record":{"id":"0202334d381550c7","repo":"redis/go-redis","slug":"to-route-commands-by-latency-use-newfailoverclust","errorCode":null,"errorMessage":"to route commands by latency, use NewFailoverClusterClient","messagePattern":"to route commands by latency, use NewFailoverClusterClient","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sentinel.go","lineNumber":549,"sourceCode":"\t// any parameters left?\n\tif r := q.remaining(); len(r) > 0 {\n\t\treturn nil, fmt.Errorf(\"redis: unexpected option: %s\", strings.Join(r, \", \"))\n\t}\n\n\treturn o, nil\n}\n\n// NewFailoverClient returns a Redis client that uses Redis Sentinel\n// for automatic failover. It's safe for concurrent use by multiple\n// goroutines.\n// Passing nil FailoverOptions will cause a panic.\nfunc NewFailoverClient(failoverOpt *FailoverOptions) *Client {\n\tif failoverOpt == nil {\n\t\tpanic(\"redis: NewFailoverClient nil options\")\n\t}\n\n\tif failoverOpt.RouteByLatency {\n\t\tpanic(\"to route commands by latency, use NewFailoverClusterClient\")\n\t}\n\tif failoverOpt.RouteRandomly {\n\t\tpanic(\"to route commands randomly, use NewFailoverClusterClient\")\n\t}\n\n\tsentinelAddrs := make([]string, len(failoverOpt.SentinelAddrs))\n\tcopy(sentinelAddrs, failoverOpt.SentinelAddrs)\n\n\trand.Shuffle(len(sentinelAddrs), func(i, j int) {\n\t\tsentinelAddrs[i], sentinelAddrs[j] = sentinelAddrs[j], sentinelAddrs[i]\n\t})\n\n\tfailover := &sentinelFailover{\n\t\topt:           failoverOpt,\n\t\tsentinelAddrs: sentinelAddrs,\n\t}\n\n\topt := failoverOpt.clientOptions()","sourceCodeStart":531,"sourceCodeEnd":567,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/sentinel.go#L531-L567","documentation":"NewFailoverClient rejects FailoverOptions.RouteByLatency=true because read-by-latency routing is only implemented by the cluster client. The sentinel failover client has no latency-based replica routing, so the constructor panics to point the developer at the correct constructor.","triggerScenarios":"Calling redis.NewFailoverClient with &redis.FailoverOptions{RouteByLatency: true, ...} set.","commonSituations":"Copy-pasting options from a ClusterClient configuration (where RouteByLatency is valid) into FailoverOptions; sharing a single options struct across multiple client types in a config layer.","solutions":["Call redis.NewFailoverClusterClient(opts) instead of NewFailoverClient when you need RouteByLatency.","Set RouteByLatency to false (or omit it) if the sentinel client is what you actually want."],"exampleFix":"// before\nclient := redis.NewFailoverClient(&redis.FailoverOptions{\n    MasterName: \"mymaster\",\n    SentinelAddrs: addrs,\n    RouteByLatency: true, // panics\n})\n\n// after\nclient := redis.NewFailoverClusterClient(&redis.FailoverOptions{\n    MasterName: \"mymaster\",\n    SentinelAddrs: addrs,\n    RouteByLatency: true,\n})","handlingStrategy":"validation","validationCode":"if opt.RouteByLatency {\n    return errors.New(\"RouteByLatency requires NewFailoverClusterClient, not NewFailoverClient\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep separate config structs for cluster and sentinel clients so routing flags cannot leak between them.","When migrating between client types, audit boolean routing flags (RouteByLatency, RouteRandomly) before changing the constructor."],"tags":["panic","sentinel","cluster","routing","misconfiguration"],"backgroundTag":"invalid-option-combination","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}