{"record":{"id":"a12917f424b62576","repo":"redis/go-redis","slug":"redis-watch-requires-at-least-one-shard","errorCode":null,"errorMessage":"redis: Watch requires at least one shard","messagePattern":"redis: Watch requires at least one shard","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ring.go","lineNumber":1006,"sourceCode":"\tif len(keys) == 0 {\n\t\treturn fmt.Errorf(\"redis: Watch requires at least one key\")\n\t}\n\n\tvar shards []*ringShard\n\n\tfor _, key := range keys {\n\t\tif key != \"\" {\n\t\t\tshard, err := c.sharding.GetByKey(key)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tshards = append(shards, shard)\n\t\t}\n\t}\n\n\tif len(shards) == 0 {\n\t\treturn fmt.Errorf(\"redis: Watch requires at least one shard\")\n\t}\n\n\tif len(shards) > 1 {\n\t\tfor _, shard := range shards[1:] {\n\t\t\tif shard.Client != shards[0].Client {\n\t\t\t\terr := fmt.Errorf(\"redis: Watch requires all keys to be in the same shard\")\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn shards[0].Client.Watch(ctx, fn, keys...)\n}\n\n// Close closes the ring client, releasing any open resources.\n//\n// It is rare to Close a Ring, as the Ring is meant to be long-lived\n// and shared between many goroutines.","sourceCodeStart":988,"sourceCodeEnd":1024,"githubUrl":"https://github.com/redis/go-redis/blob/c5cad058c72f58370553b48566302303cf8a2e89/ring.go#L988-L1024","documentation":"After filtering out empty strings, Ring.Watch found no shard for any of the given keys. This happens when every key maps to no shard (e.g. all keys are empty strings, or no shards have been set on the Ring) — the transaction cannot be executed anywhere.","triggerScenarios":"Calling ringClient.Watch(ctx, fn, keys...) where all keys are \"\" (skipped by the non-empty check) or the Ring has no shards configured (Set/AddShard never called or cleared), at ring.go:1006.","commonSituations":"Constructing a Ring programmatically and forgetting ring.Set/AddShard before use; keys derived from data where all values are empty strings; a Ring whose shard list was wiped by reconfiguration.","solutions":["Verify the Ring has shards: call ring.Set(...) / AddShard before Watch","Validate that keys are non-empty strings before invoking Watch","Inspect key-to-shard mapping; if using a custom hash function, confirm keys hash to a configured shard"],"exampleFix":"// before\nring := NewRing(NewRingOptions()) // no shards added\nring.Watch(ctx, fn, \"k1\")\n// after\nring := NewRing(NewRingOptions())\nring.Set(ctx, &RingShard{Client: redis.NewClient(&redis.Options{Addr: \":6379\"}), Weight: 1})\nring.Watch(ctx, fn, \"k1\")","handlingStrategy":"validation","validationCode":"if ring == nil || len(ring.shards) == 0 { /* check via ring.Get/HasShard API */ }\nfor _, k := range keys {\n    if k == \"\" { return errors.New(\"Watch: empty key\") }\n}","typeGuard":null,"tryCatchPattern":"if err := ring.Watch(ctx, fn, keys...); err != nil {\n    if strings.Contains(err.Error(), \"requires at least one shard\") {\n        return fmt.Errorf(\"ring misconfigured: %w\", err)\n    }\n    return err\n}","preventionTips":["Call ring.Set/AddShard before any Watch usage","Sanitize keys: skip or reject empty strings early","Add a startup health check that pings every Ring shard"],"tags":["go","ring","transactions","configuration"],"backgroundTag":"empty-keys-argument","analyzedSha":"c5cad058c72f58370553b48566302303cf8a2e89","analyzedAt":"2026-09-01T06:50:53.388Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}