redis/redis-py · error · WatchError
Slot rebalancing occurred while watching keys
Error message
Slot rebalancing occurred while watching keys
What it means
WatchError raised in TransactionStrategy._reinitialize_on_error (redis/asyncio/cluster.py:3303) when an ASK or MOVED redirect arrives while WATCH is active and the transaction is mid-execution. A slot move invalidates the watched-key connection pinning (WATCH state is connection-local), so the transaction cannot transparently retry. The caller must redo the whole watch/transaction sequence.
Solutions
- Wrap the watch/multi/exec sequence in a retry loop that re-runs WATCH + transaction on WatchError
- Pause or avoid cluster reshard during critical transactions
- Use optimistic concurrency at the application level (version checks) instead of WATCH under unstable topology
Example fix
// before
await pipe.watch('k1')
...multi/exec...
// after
for _ in range(retries):
try:
await pipe.watch('k1')
...multi/exec...
break
except WatchError:
await pipe.reset() Defensive patterns
Strategy: retry
Validate before calling
# cannot fully prevent; topology is external. Detect and retry. from redis.exceptions import WatchError
Try / catch
for _ in range(MAX_RETRIES):
try:
await pipe.watch('k1'); pipe.multi(); ...; await pipe.execute(); break
except WatchError:
await pipe.reset() Prevention
- Always retry the full WATCH/transaction sequence on WatchError
- Avoid running transactions during scheduled reshard windows
When it happens
Trigger: A cluster reshard, failover, or slot migration is in progress while a watched transaction runs, producing an ASK/MOVED error during EXEC.
Common situations: Operating against a cluster undergoing scaling operations; long-running transactions that outlast a rebalance window.
Related errors
- A occurred while watching one or more keys
- A occurred while watching one or more keys
- Cannot identify slot number for command
- Cannot issue a WATCH after a MULTI
- Cannot watch or send commands on different slots
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/a04ae81a131a3dac.
Report an issue: GitHub.
Appendix: source
Thrown at redis/asyncio/cluster.py:3303
if command_name in self.UNWATCH_COMMANDS:
self._watching = False
return output
async def _reinitialize_on_error(self, error, failure_count):
if hasattr(error, "connection"):
await record_error_count(
server_address=error.connection.host,
server_port=error.connection.port,
network_peer_address=error.connection.host,
network_peer_port=error.connection.port,
error_type=error,
retry_attempts=failure_count,
is_internal=True,
)
if self._watching:
if type(error) in self.SLOT_REDIRECT_ERRORS and self._executing:
raise WatchError("Slot rebalancing occurred while watching keys")
if (
type(error) in self.SLOT_REDIRECT_ERRORS
or type(error) in self.CONNECTION_ERRORS
):
if self._transaction_connection and self._transaction_node:
# Disconnect and release back to pool
await self._transaction_connection.disconnect()
self._transaction_node.release(self._transaction_connection)
self._transaction_connection = None
self._pipe.cluster_client.reinitialize_counter += 1
if (
self._pipe.cluster_client.reinitialize_steps
and self._pipe.cluster_client.reinitialize_counter
% self._pipe.cluster_client.reinitialize_steps
== 0
):View on GitHub (pinned to 6a6b581b48)