{"id":"4779af8644d5e552","repo":"redis/redis-py","slug":"method-unwatch-is-not-supported-outside-of-trans","errorCode":null,"errorMessage":"method unwatch() is not supported outside of transactional context","messagePattern":"method unwatch\\(\\) is not supported outside of transactional context","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"error","filePath":"redis/asyncio/cluster.py","lineNumber":3042,"sourceCode":"\n    async def reset(self):\n        \"\"\"\n        Reset back to empty pipeline.\n        \"\"\"\n        self._command_queue = []\n\n    def multi(self):\n        raise RedisClusterException(\n            \"method multi() is not supported outside of transactional context\"\n        )\n\n    async def watch(self, *names):\n        raise RedisClusterException(\n            \"method watch() is not supported outside of transactional context\"\n        )\n\n    async def unwatch(self):\n        raise RedisClusterException(\n            \"method unwatch() is not supported outside of transactional context\"\n        )\n\n    async def discard(self):\n        raise RedisClusterException(\n            \"method discard() is not supported outside of transactional context\"\n        )\n\n    async def unlink(self, *names):\n        if len(names) != 1:\n            raise RedisClusterException(\n                \"unlinking multiple keys is not implemented in pipeline command\"\n            )\n\n        return self.execute_command(\"UNLINK\", names[0])\n\n\nclass TransactionStrategy(AbstractStrategy):","sourceCodeStart":3024,"sourceCodeEnd":3060,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/cluster.py#L3024-L3060","documentation":"Raised unconditionally by PipelineStrategy.unwatch() — the non-transactional cluster pipeline strategy. UNWATCH clears keys watched with WATCH; since a non-transactional cluster pipeline never supported WATCH in the first place, UNWATCH has no effect and is rejected to surface the misuse.","triggerScenarios":"Calling await pipe.unwatch() on a pipeline created via rc.pipeline() or rc.pipeline(transaction=False). Only transactional pipelines (transaction=True) implement unwatch().","commonSituations":"Porting standalone Redis pipeline cleanup code (try/finally with unwatch()) to cluster mode without the transactional pipeline; calling unwatch() defensively on the wrong pipeline flavor.","solutions":["Switch to a transactional pipeline: pipe = rc.pipeline(transaction=True), then await pipe.unwatch().","If you are not using WATCH, remove the unwatch() call entirely.","Restructure so transactional-cleanup calls only run when transaction=True was used."],"exampleFix":"// before\npipe = rc.pipeline()\nawait pipe.unwatch()  # raises\n\n// after\npipe = rc.pipeline(transaction=True)\nawait pipe.unwatch()","handlingStrategy":"validation","validationCode":"async def safe_unwatch(pipe):\n    if not getattr(pipe, '_transaction', False):\n        return  # nothing to unwatch on a non-transactional pipeline\n    await pipe.unwatch()","typeGuard":"from redis.asyncio.cluster import TransactionStrategy\n\ndef supports_unwatch(pipe) -> bool:\n    return isinstance(getattr(pipe, '_strategy', None), TransactionStrategy)","tryCatchPattern":"from redis.cluster import RedisClusterException\n\ntry:\n    await pipe.unwatch()\nexcept RedisClusterException as e:\n    if 'unwatch()' in str(e):\n        pipe = rc.pipeline(transaction=True)\n        await pipe.unwatch()\n    else:\n        raise","preventionTips":["Only call unwatch() on pipelines created with transaction=True.","Gate cleanup helpers on the pipeline's transaction flag.","Use reset() to clear a non-transactional pipeline's queue."],"tags":["redis-cluster","pipeline","transaction","unwatch"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}