The "ERR crossed slot boundary" (also displayed as "CROSSSLOT Keys in request don't hash to the same slot") error occurs in Redis Cluster when a multi-key command attempts to operate on keys that hash to different hash slots. Redis Cluster divides data across 16,384 slots, and multi-key operations like MGET, MSET, and Lua scripts require all keys to reside in the same slot. Fix by using hash tags to ensure keys hash to the same slot, or by executing separate commands on each slot.
Redis Cluster uses hash slots to distribute data across multiple nodes. When you execute a multi-key command (MGET, MSET, DEL, etc.), all keys involved must hash to the same slot so the command can be executed atomically on a single node. If keys hash to different slots, Redis rejects the command with "ERR crossed slot boundary" or "CROSSSLOT Keys in request don't hash to the same slot". This restriction exists because Redis Cluster does not support cross-slot transactions or distributed operations. The error acts as a safety mechanism to prevent undefined behavior when operations would need to coordinate across multiple nodes.
Use the CLUSTER KEYSLOT command to determine which slot each key hashes to. This will show you which keys are causing the crossed slot boundary error.
redis-cli -c CLUSTER KEYSLOT key1
redis-cli -c CLUSTER KEYSLOT key2
redis-cli -c CLUSTER KEYSLOT key3Each command returns an integer representing the slot (0-16383). If the slots differ, that's why your multi-key command is failing.
Example:
> CLUSTER KEYSLOT user:1001:name
(integer) 5598
> CLUSTER KEYSLOT user:1001:email
(integer) 7967In this case, the two keys hash to different slots (5598 vs 7967), so commands like MGET user:1001:name user:1001:email will fail.
Redis Cluster uses hash tags (substrings in curly braces) to allow multiple keys to map to the same slot. Only the substring within {} is used for slot calculation.
# WRONG - keys hash to different slots
MGET user:1001:name user:1001:email
# CORRECT - use hash tags so all keys hash to the same slot
MGET user:{1001}:name user:{1001}:emailWith hash tags, the slot is calculated based only on "1001", so both keys will hash to the same slot. This allows the MGET command to succeed.
Examples:
# All these keys hash to the same slot (based on "user123")
SET user:{user123}:name "Alice"
SET user:{user123}:email "[email protected]"
SET user:{user123}:age 30
MGET user:{user123}:name user:{user123}:email user:{user123}:ageGuidelines for hash tags:
- Wrap the identifying portion of the key in {curly braces}
- Only the portion inside {} is hashed
- Everything outside {} is ignored for slot calculation
- Keep the identifying portion short and consistent
If hash tags aren't feasible for your schema, execute separate commands for each slot. This requires knowing which slot each key belongs to.
// WRONG - fails with CROSSSLOT error
const values = await redis.mget(['key1', 'key2', 'key3']);
// CORRECT - execute commands per slot
async function multiGetBySlot(keys) {
const keysBySlot = {};
// Group keys by their slot
for (const key of keys) {
const slot = await redis.clusterKeyslot(key);
if (!keysBySlot[slot]) keysBySlot[slot] = [];
keysBySlot[slot].push(key);
}
// Execute MGET for each slot separately
const results = [];
for (const slot in keysBySlot) {
const slotKeys = keysBySlot[slot];
const slotResults = await redis.mget(slotKeys);
results.push(...slotResults);
}
return results;
}This approach works but is more complex and slower than keeping keys in the same slot.
Lua scripts used with EVAL/EVALSHA must declare all keys they'll access. If keys are in different slots, the command will fail.
# WRONG - script accesses keys in different slots
EVAL "return {redis.call('GET', KEYS[1]), redis.call('GET', KEYS[2])}" 0 user:name user:email
# CORRECT - use hash tags to ensure keys are in same slot
EVAL "return {redis.call('GET', KEYS[1]), redis.call('GET', KEYS[2])}" 2 user:{123}:name user:{123}:emailThe key insight: all keys declared in KEYS[...] must hash to the same slot. Use hash tags consistently throughout your Lua scripts.
Example with proper hash tags:
-- Lua script with hash tags
local name = redis.call('GET', KEYS[1])
local email = redis.call('GET', KEYS[2])
local age = redis.call('GET', KEYS[3])
return {name, email, age}Call it with:
EVAL "..." 3 user:{1001}:name user:{1001}:email user:{1001}:ageSome Redis client libraries provide automatic handling for cross-slot commands. Check your client's documentation.
For JavaScript (ioredis):
const redis = require('ioredis');
const cluster = new redis.Cluster([
{ host: 'localhost', port: 6379 },
]);
// ioredis with enableAutoPipelining can handle this
const values = await cluster.mget(['key1', 'key2', 'key3']);For Go (rueidis):
// rueidis v0.0.94+ has an MGet helper for cross-slot commands
results, err := c.Do(ctx, c.B().Mget().Keys(key1, key2, key3).Build()).AsStrSlice()For Java (Lettuce):
// Lettuce automatically decomposes multi-key commands across slots
List<String> values = commands.mget(key1, key2, key3);If your client library supports it, enable automatic cross-slot handling. Otherwise, use hash tags or manual slot management.
If your use case genuinely requires arbitrary multi-key operations across slots without hash tags, consider these options:
Redis Enterprise supports these multi-key commands across slots:
- MGET
- MSET
- DEL
- EXISTS
- UNLINK
- TOUCH
Switch to Redis Enterprise if you have many operations that require cross-slot access and cannot be redesigned with hash tags.
Standalone Redis (non-cluster) has no slot restrictions:
- If you don't need cluster mode's scalability, use standalone Redis for simpler operations
- Cluster mode is for horizontal scaling when you need it
Evaluate your requirements:
- Do you need to scale across multiple nodes? → Use Cluster with hash tags
- Do you need arbitrary multi-key operations? → Use Enterprise or Standalone
- Can you redesign your schema with hash tags? → Use standard Cluster mode (cost-effective)
Hash Slot Distribution: Redis Cluster automatically divides the 16,384 hash slots across nodes. When you add or remove nodes, slots are rebalanced. The slot for a key is calculated as CRC16(key) mod 16384, where CRC16 uses only the portion in curly braces if hash tags are present.
Hash Tag Limitations: Hash tags only affect slot calculation. The portion outside {} is still part of the key name, so {user123}:name and {user123}:email are different keys with the same slot. You can use multiple levels of nesting, but only the innermost {curly} portion is hashed.
Performance Implications: Using hash tags keeps related keys on the same node, which can create hotspots if one user or entity has many keys. Monitor slot distribution and consider resharding if one slot becomes too hot.
Cluster Resharding: During resharding, keys move between nodes. The CLUSTER KEYSLOT command will still return the correct slot after resharding; it's based on the key itself, not the node location.
Redis OSS vs Enterprise: Open source Redis Cluster is strict about slot boundaries. Redis Enterprise adds a "Conflict-free Replicated Data Type" (CRDT) mode that allows some cross-slot operations. Check your deployment's capabilities.
Debugging Tools: Use redis-cli with the -c (cluster) flag to connect to any node in the cluster. It will redirect you automatically. Use CLUSTER INFO to see overall cluster state and CLUSTER NODES to see slot distribution across nodes.
ERR fsync error
How to fix "ERR fsync error" in Redis
CLUSTERDOWN The cluster is down
How to fix 'CLUSTERDOWN The cluster is down' in Redis
ERR Job for redis-server.service failed because a timeout was exceeded
Job for redis-server.service failed because a timeout was exceeded
ERR Unbalanced XREAD list of streams
How to fix "ERR Unbalanced XREAD list" in Redis
ERR syntax error
How to fix "ERR syntax error" in Redis