go-redis/redis · error
redis: Watch requires all keys to be in the same shard
Error message
redis: Watch requires all keys to be in the same shard
What it means
Returned by Ring.Watch when the supplied keys hash to more than one distinct shard. Ring uses consistent hashing across independent Redis nodes; a MULTI/EXEC transaction can only lock keys that live on the same node, so cross-shard key sets are rejected.
Source
Thrown at ring.go:988
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.
func (c *Ring) Close() error {
c.heartbeatCancelFn()
return c.sharding.Close()
}
View on GitHub (pinned to 36d97525cd)
Solutions
- Co-locate transactional keys on the same shard using a Redis hash tag, e.g. {user1}:profile and {user1}:cart, so they route together.
- If co-location is not possible, split the transaction into per-shard transactions or use a single-shard client instead of a Ring.
- Review the Ring's shard count and hashing to understand which keys collide.
Example fix
// before
keys := []string{"user:1:profile", "user:1:cart"} // may hash to different shards
err := ring.Watch(ctx, fn, keys...)
// after
keys := []string{"{user:1}:profile", "{user:1}:cart"} // same hash tag -> same shard
err := ring.Watch(ctx, fn, keys...) Defensive patterns
Strategy: validation
Validate before calling
// Ensure all transactional keys share a hash tag before Watch.
if len(keys) > 1 {
tag := internal.Hashtag(keys[0])
for _, k := range keys[1:] {
if internal.Hashtag(k) != tag || tag == "" {
return errors.New("keys must share a {hashtag} to be co-located on a Ring shard")
}
}
} Try / catch
if err := ring.Watch(ctx, fn, keys...); err != nil {
if strings.Contains(err.Error(), "same shard") {
// re-issue keys with a shared {tag} or split into per-shard transactions
}
} Prevention
- Design multi-key access around a shared {entity-id} hash tag.
- Document which key patterns are transactional so future authors keep the tag.
- Prefer a single-node Client if you need unconstrained multi-key transactions.
When it happens
Trigger: Calling ring.Watch(ctx, fn, keys...) with two or more keys whose hash slots route to different ring shards (different Ring client instances / different Redis nodes).
Common situations: Multi-key transaction on a Ring without co-locating keys via hash tags; refactoring a single-node client to a Ring without adjusting transactional access patterns; assuming keys on the same prefix hash together (they do not, unless wrapped in {tag}).
Related errors
- redis: the shard is not in the ring
- redis: Watch requires at least one key
- redis: Watch requires at least one shard
- redis: all ring shards are down
- redis: AutoPipeline is not supported by Ring
AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06).
Data as JSON: /data/errors/e7fa732bc26b2a33.json.
Report an issue: GitHub.