redis/redis-py · error · CrossSlotTransactionError

All keys involved in a cluster transaction must map to the s

Error message

All keys involved in a cluster transaction must map to the same slot

What it means

Raised as CrossSlotTransactionError at the top of _execute_transaction when len(self._pipeline_slots) > 1. The buffered (deferred) branch of _execute_command collects every queued command's slot; at execute time, if more than one distinct slot is present, the library cannot wrap them in a single MULTI/EXEC (which is single-node in Redis Cluster).

Source

Thrown at redis/asyncio/cluster.py:3346

        return await self._execute_transaction_with_retries(stack, raise_on_error)

    async def _execute_transaction_with_retries(
        self, stack: List["PipelineCommand"], raise_on_error: bool
    ):
        return await self._retry.call_with_retry(
            lambda: self._execute_transaction(stack, raise_on_error),
            lambda error, failure_count: self._reinitialize_on_error(
                error, failure_count
            ),
            with_failure_count=True,
        )

    async def _execute_transaction(
        self, stack: List["PipelineCommand"], raise_on_error: bool
    ):
        if len(self._pipeline_slots) > 1:
            raise CrossSlotTransactionError(
                "All keys involved in a cluster transaction must map to the same slot"
            )

        self._executing = True

        redis_node, connection = self._get_client_and_connection_for_transaction()
        # Only disconnect if not watching - disconnecting would lose WATCH state
        if not self._watching:
            await redis_node.disconnect_if_needed(connection)

        # Ensure fieldsets referenced by buffered HIMPORT SETs are prepared on this
        # node's connection before the MULTI/EXEC block (session state, not
        # transactional). All keys share one slot here, so it is a single node.
        await redis_node._himport_prepare_pipeline(connection, stack)

        stack = chain(
            [PipelineCommand(0, "MULTI")],
            stack,

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Add a shared hash tag to all keys in the transaction so they map to one slot (e.g. 'user:{42}', 'acct:{42}').
  2. Split the work into multiple single-slot transactions, one per slot.
  3. If atomicity is not required, use the non-transactional cluster pipeline (client.pipeline(transaction=False)) which fans out across slots.

Example fix

// before
pipe = client.pipeline(transaction=True)
await pipe.set('user:1', 'a')
await pipe.set('user:2', 'b')  # different slot
await pipe.execute()  # raises [86]
// after
pipe = client.pipeline(transaction=True)
await pipe.set('user:{1}', 'a')
await pipe.set('acct:{1}', 'b')   # same slot via tag
await pipe.execute()
Defensive patterns

Strategy: validation

Validate before calling

from redis.cluster import key_slot
from collections import defaultdict
buckets = defaultdict(list)
for k, v in mapping.items():
    buckets[key_slot(k.encode())].append((k, v))
# then one transaction per slot, or use hash tags to collapse to one

Try / catch

from redis.exceptions import CrossSlotTransactionError
try:
    await pipe.execute()
except CrossSlotTransactionError:
    # split commands by slot and execute one transaction per slot

Prevention

When it happens

Trigger: Queueing several deferred commands (the default pipeline path before execute()) whose keys hash to different slots, then calling execute() with transaction=True on a cluster pipeline. Example: pipe.set('a',1); pipe.set('b',2); await pipe.execute() with 'a' and 'b' in different slots.

Common situations: Treating a cluster pipeline like a standalone pipeline and batching unrelated keys; forgetting hash tags for keys that must be transacted together.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/9cefd59b7f148014.json. Report an issue: GitHub.