{"id":"6fc0887133e17e86","repo":"redis/redis-py","slug":"cannot-issue-a-watch-after-a-multi-6fc088","errorCode":null,"errorMessage":"Cannot issue a WATCH after a MULTI","messagePattern":"Cannot issue a WATCH after a MULTI","errorType":"exception","errorClass":"RedisError","httpStatus":null,"severity":"error","filePath":"redis/client.py","lineNumber":2275,"sourceCode":"            )\n            raise\n\n        finally:\n            # in reset() the connection is disconnected before returned to the pool if\n            # it is marked for reconnect.\n            self.reset()\n\n    def discard(self):\n        \"\"\"\n        Flushes all previously queued commands\n        See: https://redis.io/commands/DISCARD\n        \"\"\"\n        self.execute_command(\"DISCARD\")\n\n    def watch(self, *names):\n        \"\"\"Watches the values at keys ``names``\"\"\"\n        if self.explicit_transaction:\n            raise RedisError(\"Cannot issue a WATCH after a MULTI\")\n        return self.execute_command(\"WATCH\", *names)\n\n    def unwatch(self) -> bool:\n        \"\"\"Unwatches all previously specified keys\"\"\"\n        return self.watching and self.execute_command(\"UNWATCH\") or True\n","sourceCodeStart":2257,"sourceCodeEnd":2281,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/client.py#L2257-L2281","documentation":"Raised in `Pipeline.watch()` when `self.explicit_transaction` is already True. WATCH must precede MULTI; once a MULTI block has started Redis forbids WATCH until after EXEC/DISCARD. redis-py enforces this client-side by checking the explicit_transaction flag and raising RedisError.","triggerScenarios":"Calling `pipe.multi()` and then `pipe.watch('k')` on the same pipeline. Also reachable by accidentally inverting the order in a helper: multi() then watch() instead of watch() then multi().","commonSituations":"Misordered transaction setup; refactoring that moved watch() after the multi() call; copy-paste errors.","solutions":["Issue WATCH before MULTI: `pipe.watch(...); pipe.multi(); ...commands...; pipe.execute()`.","If you need to watch different keys, start a new pipeline and re-do WATCH->MULTI from scratch.","Call `pipe.discard()` to abort the current MULTI, then re-establish WATCH in the correct order on a fresh pipeline."],"exampleFix":"# before\npipe = client.pipeline()\npipe.multi()\npipe.watch('k')  # raises: Cannot issue a WATCH after a MULTI\n\n# after\npipe = client.pipeline()\npipe.watch('k')\npipe.multi()\npipe.set('k', 'v')\npipe.execute()","handlingStrategy":"validation","validationCode":"# Enforce correct ordering: WATCH before MULTI\npipe = client.pipeline()\npipe.watch('k')  # always before multi()\npipe.multi()","typeGuard":null,"tryCatchPattern":"from redis.exceptions import RedisError\ntry:\n    pipe.watch('k')\nexcept RedisError as e:\n    if 'Cannot issue a WATCH after a MULTI' in str(e):\n        pipe = client.pipeline(); pipe.watch('k'); pipe.multi()\n    else:\n        raise","preventionTips":["Sequence WATCH before MULTI, then commands, then EXEC.","Start a fresh pipeline when changing the watched key set."],"tags":["pipeline","transaction","watch","ordering"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}