redis/redis-py · error · CrossSlotTransactionError
Cannot watch or send commands on different slots
Error message
Cannot watch or send commands on different slots
What it means
Raised as CrossSlotTransactionError inside _execute_command when the pipeline is in WATCH or immediate-execute mode and a newly queued command's slot differs from the slots already accumulated in _pipeline_slots. Redis Cluster transactions are atomic only within one hash slot, so the library refuses to mix slots in a watched/immediate path.
Source
Thrown at redis/asyncio/cluster.py:3152
async def _execute_command(
self, *args: Union[KeyT, EncodableT], **kwargs: Any
) -> Any:
if self._pipe.cluster_client._initialize:
await self._pipe.cluster_client.initialize()
slot_number: Optional[int] = None
if args[0] not in self.NO_SLOTS_COMMANDS:
slot_number = await self._pipe.cluster_client._determine_slot(*args)
if (
self._watching or args[0] in self.IMMEDIATE_EXECUTE_COMMANDS
) and not self._explicit_transaction:
if args[0] == "WATCH":
self._validate_watch()
if slot_number is not None:
if self._pipeline_slots and slot_number not in self._pipeline_slots:
raise CrossSlotTransactionError(
"Cannot watch or send commands on different slots"
)
self._pipeline_slots.add(slot_number)
elif args[0] not in self.NO_SLOTS_COMMANDS:
raise RedisClusterException(
f"Cannot identify slot number for command: {args[0]},"
"it cannot be triggered in a transaction"
)
return self._immediate_execute_command(*args, **kwargs)
else:
if slot_number is not None:
self._pipeline_slots.add(slot_number)
return super().execute_command(*args, **kwargs)
def _validate_watch(self):View on GitHub (pinned to da03cdc7e8)
Solutions
- Ensure every key in the transaction shares a hash tag, e.g. 'user:{1}' and 'account:{1}' so they land in the same slot.
- Restructure so all watched + transacted keys belong to one slot; split multi-entity work into per-slot transactions.
- If cross-slot atomicity is truly required, move that workflow to a Redis that supports it (single instance, or functional-redis-style multi-key commands like Lua/MULTI on one shard) — not a cluster transaction.
Example fix
// before
await pipe.watch('user:1')
await pipe.get('user:2') # different slot -> raises [81]
// after
await pipe.watch('user:{1}')
await pipe.get('acct:{1}') # same slot via hash tag Defensive patterns
Strategy: validation
Validate before calling
from redis.cluster import key_slot
slots = {key_slot(k.encode()) for k in keys_to_watch_or_use}
if len(slots) > 1:
raise ValueError('All keys must share a slot; use hash tags') Try / catch
from redis.exceptions import CrossSlotTransactionError
try:
await pipe.execute()
except CrossSlotTransactionError:
# re-bucket keys by hash tag and retry per-slot Prevention
- Use {hashtag} on keys that must be transacted together.
- Validate slot equality before adding commands to a watched pipeline.
When it happens
Trigger: After WATCH('k1') (or after queuing a key command that fixed the transaction's slot), calling another immediate-execute command whose key hashes to a different slot, e.g. WATCH('user:1') then pipeline.get('user:2') where the two keys are not hash-tagged to the same slot.
Common situations: Forgetting to use Redis hash tags ({tag}) on keys that must be transacted together; mixing keys with different prefixes in a single WATCH/MULTI block; copying standalone-pipeline code into a cluster pipeline.
Related errors
- All keys involved in a cluster transaction must map to the s
- Cannot watch or send commands on different slots
- At least a command with a key is needed to identify a node
- Cannot identify slot number for command: {args[0]},it cannot
- Cannot issue a WATCH after a MULTI
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/38bdd069a0735e8e.json.
Report an issue: GitHub.