go-redis/redis · error
redis: Watch requires at least one shard
Error message
redis: Watch requires at least one shard
What it means
Returned by Ring.Watch when keys were supplied but none of them resolved to a shard. Watch filters out empty-string keys before resolving shards; if every key is "" the shards slice stays empty and the transaction cannot proceed because there is no shard to lock.
Source
Thrown at ring.go:982
if len(keys) == 0 {
return fmt.Errorf("redis: Watch requires at least one key")
}
var shards []*ringShard
for _, key := range keys {
if key != "" {
shard, err := c.sharding.GetByKey(key)
if err != nil {
return err
}
shards = append(shards, shard)
}
}
if len(shards) == 0 {
return fmt.Errorf("redis: Watch requires at least one shard")
}
if len(shards) > 1 {
for _, shard := range shards[1:] {
if shard.Client != shards[0].Client {
err := fmt.Errorf("redis: Watch requires all keys to be in the same shard")
return err
}
}
}
return shards[0].Client.Watch(ctx, fn, keys...)
}
// Close closes the ring client, releasing any open resources.
//
// It is rare to Close a Ring, as the Ring is meant to be long-lived
// and shared between many goroutines.View on GitHub (pinned to 36d97525cd)
Solutions
- Sanitize the keys slice: drop empty strings and verify at least one non-empty key remains before calling Watch.
- Fix the upstream producer of the keys so it never yields empty values.
- Add a unit test asserting keys are non-empty at the Watch call site.
Example fix
// before
keys := []string{"", "", ""}
err := ring.Watch(ctx, fn, keys...)
// after
var nonEmpty []string
for _, k := range keys {
if k != "" {
nonEmpty = append(nonEmpty, k)
}
}
if len(nonEmpty) == 0 {
return errors.New("no valid keys")
}
err := ring.Watch(ctx, fn, nonEmpty...) Defensive patterns
Strategy: validation
Validate before calling
var nonEmpty []string
for _, k := range keys {
if k != "" {
nonEmpty = append(nonEmpty, k)
}
}
if len(nonEmpty) == 0 {
return errors.New("no non-empty keys to watch")
}
return ring.Watch(ctx, fn, nonEmpty...) Try / catch
if err := ring.Watch(ctx, fn, keys...); err != nil {
if strings.Contains(err.Error(), "requires at least one shard") {
// all keys were empty; sanitize upstream
}
} Prevention
- Sanitize key lists at the boundary where they enter your code.
- Reject empty keys at the data-ingestion layer rather than at the Redis call site.
- Add property tests asserting keys are non-empty before Watch.
When it happens
Trigger: Calling ring.Watch(ctx, fn, keys...) where every element of keys is an empty string. The loop appends a shard only for non-empty keys, so an all-empty slice yields zero shards.
Common situations: Keys slice populated from a struct/JSON that had missing fields; a map iteration producing zero-length values; a trim/normalization step that reduced all keys to ""; accidental placeholder keys.
Related errors
- redis: Watch requires at least one key
- redis: Watch requires all keys to be in the same shard
- at least one channel is required
- GeoRadius does not support Store or StoreDist
- GeoRadiusStore requires Store or StoreDist
AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06).
Data as JSON: /data/errors/b0f116fbecac765b.json.
Report an issue: GitHub.