Architecture

Redis "CROSSSLOT Keys in request don't hash to the same slot": Hash Tags Done Right

Code that ran fine on a single Redis breaks the day you move to Redis Cluster or Valkey Cluster. The CROSSSLOT error is the reason, and hash tags are the fix, as long as you do not pile a whole dataset into one slot.

· 10 min read · AG Data Team

Redis "CROSSSLOT Keys in request don't hash to the same slot" is the error you get when one command, transaction or script touches keys that live in different hash slots of a Redis Cluster. On a single instance the same code works. In a cluster, keys are spread across nodes, and Redis refuses multi-key operations it cannot run on one node atomically. Valkey Cluster returns exactly the same message.

Why the error exists

The Redis Cluster specification splits the key space into 16384 hash slots, with HASH_SLOT = CRC16(key) mod 16384. Each primary owns a subset of slots. A command such as MGET a b is only safe if both keys are on the same node, and Redis checks this at the slot level: all keys in the request must map to one slot, not merely to one node. When they do not, the node replies with CROSSSLOT instead of executing anything.

127.0.0.1:7000> MGET user:1000:name user:1001:name
(error) CROSSSLOT Keys in request don't hash to the same slot

127.0.0.1:7000> CLUSTER KEYSLOT user:1000:name
(integer) ...
127.0.0.1:7000> CLUSTER KEYSLOT user:1001:name
(integer) ...    # a different slot

Where it shows up

The fix: hash tags

A hash tag tells Redis to hash only part of the key. The spec gives the exact rules: if the key contains {, there is a } to its right, and at least one character between the first { and the first } after it, then only that substring is hashed.

{user:1000}.name       -> hashes "user:1000"
{user:1000}.cart       -> hashes "user:1000"   (same slot)
foo{}{bar}             -> hashes the whole key (empty first tag)
foo{{bar}}zap          -> hashes "{bar"
foo{bar}{zap}          -> hashes "bar" (only the first tag counts)

So the typical fix is to put the entity ID in braces for keys that must be used together:

# before: keys land in random slots
MSET user:1000:name Anna user:1000:email anna@example.com

# after: one hash tag, one slot, MSET works in cluster
MSET {user:1000}:name Anna {user:1000}:email anna@example.com

# check before you deploy
CLUSTER KEYSLOT {user:1000}:name
CLUSTER KEYSLOT {user:1000}:email

CLUSTER KEYSLOT shows the slot for any key name. Put it into a test, so a renamed key does not quietly break a transaction later.

Other ways out

  1. Split the request by slot. If you do not need atomicity, run one command per slot. Some clients do this for you; for example, redis-py's cluster client has mget_nonatomic and mset_nonatomic, which group keys by slot and send one MGET or MSET per slot. The name is honest: the result is no longer atomic.
  2. Rethink the data model. Several keys that always change together are often better as one hash. A user profile in HSET {user:1000} name ... email ... needs no cross-key atomicity at all.
  3. Script flag allow-cross-slot-keys. Redis 7+ scripts and functions can declare this flag to access undeclared keys from several slots. The docs say declared keys must still hash to one slot, and call cross-slot access discouraged; in the source, a key served by another node still fails. Treat it as a migration aid, not a design.
Do not "solve" CROSSSLOT by putting one constant tag on everything. A prefix like {app}: on every key sends the whole dataset into a single slot, so a single node holds all of it and takes all the traffic. The cluster still runs, but you are back to one server, with extra moving parts.

Hash tags on large datasets

If the cluster itself stops serving slots, that is a different problem: see our guide to Redis CLUSTERDOWN.

Key takeaways

Redis Valkey Redis Cluster Hash Tags
Work with us

Moving to Redis Cluster?

We review your key design, multi-key commands and scripts before the migration, so the cluster spreads load evenly and nothing breaks on the first deploy.

Get in touch More from the blog