{"id":"a04ae81a131a3dac","repo":"redis/redis-py","slug":"slot-rebalancing-occurred-while-watching-keys","errorCode":null,"errorMessage":"Slot rebalancing occurred while watching keys","messagePattern":"Slot rebalancing occurred while watching keys","errorType":"exception","errorClass":"WatchError","httpStatus":null,"severity":"error","filePath":"redis/asyncio/cluster.py","lineNumber":3271,"sourceCode":"        if command_name in self.UNWATCH_COMMANDS:\n            self._watching = False\n        return output\n\n    async def _reinitialize_on_error(self, error, failure_count):\n        if hasattr(error, \"connection\"):\n            await record_error_count(\n                server_address=error.connection.host,\n                server_port=error.connection.port,\n                network_peer_address=error.connection.host,\n                network_peer_port=error.connection.port,\n                error_type=error,\n                retry_attempts=failure_count,\n                is_internal=True,\n            )\n\n        if self._watching:\n            if type(error) in self.SLOT_REDIRECT_ERRORS and self._executing:\n                raise WatchError(\"Slot rebalancing occurred while watching keys\")\n\n        if (\n            type(error) in self.SLOT_REDIRECT_ERRORS\n            or type(error) in self.CONNECTION_ERRORS\n        ):\n            if self._transaction_connection and self._transaction_node:\n                # Disconnect and release back to pool\n                await self._transaction_connection.disconnect()\n                self._transaction_node.release(self._transaction_connection)\n                self._transaction_connection = None\n\n            self._pipe.cluster_client.reinitialize_counter += 1\n            if (\n                self._pipe.cluster_client.reinitialize_steps\n                and self._pipe.cluster_client.reinitialize_counter\n                % self._pipe.cluster_client.reinitialize_steps\n                == 0\n            ):","sourceCodeStart":3253,"sourceCodeEnd":3289,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/cluster.py#L3253-L3289","documentation":"Raised as WatchError inside _reinitialize_on_error when the pipeline is in WATCH mode, execution is in flight, and a slot-redirect error (MOVED/ASK — i.e. SLOT_REDIRECT_ERRORS) is returned. A redirect means the slot (and therefore the watched key) has moved to a different node mid-transaction; the WATCH state cannot be migrated, so the transaction is aborted.","triggerScenarios":"A cluster resharding/rebalance happens between WATCH and EXEC of a cluster pipeline transaction; the watched key's slot is migrated to another node and the server replies MOVED/ASK during execution.","commonSituations":"Long-running watched transactions during cluster scaling operations, slot migrations, or failover; using cluster transactions against a topology that is being actively rebalanced.","solutions":["Retry the whole WATCH/MULTI/EXEC sequence from the top — the client will refresh slot maps on the next initialize().","Keep watched transactions short to shrink the window during which a reshard can interfere.","If resharding is ongoing, pause it or run the workload outside the rebalance window."],"exampleFix":"// before (single attempt, fails under reshard)\nawait pipe.watch('k')\nawait pipe.multi(); await pipe.set('k','v'); await pipe.execute()\n// after (retry the entire block)\nfor _ in range(retries):\n    try:\n        pipe = client.pipeline(transaction=True)\n        await pipe.watch('k')\n        await pipe.multi(); await pipe.set('k','v')\n        await pipe.execute(); break\n    except WatchError:\n        continue","handlingStrategy":"retry","validationCode":"# Nothing to validate pre-flight; resharding is external. Just budget retries.\nmax_reshard_retries = 5","typeGuard":null,"tryCatchPattern":"from redis.exceptions import WatchError\nfor _ in range(max_reshard_retries):\n    try:\n        await run_watched_transaction(client)\n        break\n    except WatchError as e:\n        if 'Slot rebalancing' in str(e):\n            await client.initialize()  # refresh slot map\n            continue\n        raise","preventionTips":["Keep WATCH-to-EXEC windows short during cluster scaling.","Wrap watched transactions in a retry loop that refreshes the slot map."],"tags":["cluster","pipeline","watch","resharding","moved","retry"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}