{"id":"a91fd7d3b3850ce9","repo":"redis/redis-py","slug":"method-unwatch-is-not-supported-outside-of-trans-a91fd7","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/cluster.py","lineNumber":4540,"sourceCode":"        return nodes\n\n    def multi(self):\n        raise RedisClusterException(\n            \"method multi() is not supported outside of transactional context\"\n        )\n\n    def discard(self):\n        raise RedisClusterException(\n            \"method discard() is not supported outside of transactional context\"\n        )\n\n    def watch(self, *names):\n        raise RedisClusterException(\n            \"method watch() is not supported outside of transactional context\"\n        )\n\n    def unwatch(self, *names):\n        raise RedisClusterException(\n            \"method unwatch() is not supported outside of transactional context\"\n        )\n\n    def delete(self, *names):\n        if len(names) != 1:\n            raise RedisClusterException(\n                \"deleting multiple keys is not implemented in pipeline command\"\n            )\n\n        return self.execute_command(\"DEL\", names[0])\n\n    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])","sourceCodeStart":4522,"sourceCodeEnd":4558,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/cluster.py#L4522-L4558","documentation":"PipelineStrategy.unwatch() (redis/cluster.py:4539) always raises. UNWATCH clears keys watched in a transactional context; on a non-transactional pipeline (transaction=False) there is nothing watched and the call is invalid.","triggerScenarios":"Calling pipe.unwatch() on a pipeline created with rc.pipeline(transaction=False). Note ClusterPipeline.unwatch (redis/cluster.py:3738) takes no arguments, while the raising stub declares unwatch(self, *names) — the call still lands on the stub regardless of arguments.","commonSituations":"Cleanup code that calls unwatch() in a finally block, run against a non-transactional pipeline. Standalone pipeline patterns reused in cluster code with transaction disabled.","solutions":["Only call unwatch() on a transactional pipeline (rc.pipeline(transaction=True)).","For a non-transactional pipeline, simply reset() or let it go out of scope; no watch state exists to clear.","Guard unwatch() with the pipeline mode when sharing code between modes."],"exampleFix":"# before\npipe = rc.pipeline(transaction=False)\npipe.unwatch()  # raises\n\n# after\npipe = rc.pipeline(transaction=True)\npipe.watch('k')\npipe.unwatch()  # valid on a transactional pipeline","handlingStrategy":"validation","validationCode":"def safe_unwatch(pipe):\n    if getattr(pipe, '_execution_strategy', None).__class__.__name__ != 'TransactionStrategy':\n        return  # nothing watched on a non-transactional pipeline\n    pipe.unwatch()","typeGuard":null,"tryCatchPattern":"from redis.exceptions import RedisClusterException\n\ntry:\n    pipe.unwatch()\nexcept RedisClusterException:\n    pass  # nothing to clear on a non-transactional pipeline","preventionTips":["Only call unwatch() on a transactional pipeline.","In finally blocks, check the pipeline mode before calling transactional helpers.","Prefer reset() for non-transactional pipelines."],"tags":["cluster","pipeline","transaction","optimistic-locking"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}