{"record":{"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/6a6b581b48225afa0b76912d1028c6035baee932/redis/client.py#L2257-L2281","documentation":"Raised by Pipeline.watch() when self.explicit_transaction is True — i.e. WATCH is called after MULTI has already started. Redis protocol forbids this: WATCH must precede MULTI. The guard at client.py:2274 catches the misuse client-side before sending anything.","triggerScenarios":"Calling pipe.multi() then pipe.watch(...). The explicit_transaction flag is already True, so watch() raises immediately.","commonSituations":"Reordering transaction construction (calling multi before watch); helper functions that call watch unconditionally; refactoring code that previously used pipeline(transaction=True) auto-mode.","solutions":["Call watch() BEFORE multi(): the correct order is WATCH → (read) → MULTI → (queue) → EXEC.","Or use pipeline(transaction=True) and call watch() before execute() — auto-MULTI is issued internally at execute time, so watch() is always pre-MULTI.","Reset the pipeline and reorder if you called multi() too early."],"exampleFix":"// before\npipe = r.pipeline()\npipe.multi()\npipe.watch('k')  # RedisError: Cannot issue a WATCH after a MULTI\n\n// after\npipe = r.pipeline()\npipe.watch('k')\nv = pipe.get('k')\npipe.multi()\npipe.set('k', transform(v))\npipe.execute()","handlingStrategy":"validation","validationCode":"if pipe.explicit_transaction:\n    raise RuntimeError('Cannot WATCH after MULTI; call watch() before multi()')\npipe.watch('k')","typeGuard":"def pipeline_can_watch(pipe) -> bool:\n    return not pipe.explicit_transaction","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.reset()\n        pipe.watch('k')  # retry in correct order\n    else:\n        raise","preventionTips":["Order: WATCH before MULTI, always.","Use pipeline(transaction=True) and call watch() before execute() so MULTI is auto-issued after WATCH.","Reset the pipeline if you accidentally called multi() first."],"tags":["pipeline","transaction","watch","api-misuse"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}