{"id":"92ddfc6fce316a8b","repo":"redis/redis-py","slug":"commands-without-an-initial-watch-have-already-bee","errorCode":null,"errorMessage":"Commands without an initial WATCH have already been issued","messagePattern":"Commands without an initial WATCH have already been issued","errorType":"exception","errorClass":"RedisError","httpStatus":null,"severity":"error","filePath":"redis/asyncio/client.py","lineNumber":1897,"sourceCode":"            # release itself so a second cancel cannot split the pool's\n            # internal in-use/available bookkeeping mid-update.\n            if self.connection:\n                connection, self.connection = self.connection, None\n                await asyncio.shield(self.connection_pool.release(connection))\n\n    async def aclose(self) -> None:\n        \"\"\"Alias for reset(), a standard method name for cleanup\"\"\"\n        await self.reset()\n\n    def multi(self):\n        \"\"\"\n        Start a transactional block of the pipeline after WATCH commands\n        are issued. End the transactional block with `execute`.\n        \"\"\"\n        if self.explicit_transaction:\n            raise RedisError(\"Cannot issue nested calls to MULTI\")\n        if self.command_stack:\n            raise RedisError(\n                \"Commands without an initial WATCH have already been issued\"\n            )\n        self.explicit_transaction = True\n\n    def execute_command(\n        self, *args, **kwargs\n    ) -> Union[\"Pipeline\", Awaitable[\"Pipeline\"]]:\n        if (self.watching or args[0] == \"WATCH\") and not self.explicit_transaction:\n            return self.immediate_execute_command(*args, **kwargs)\n        return self.pipeline_execute_command(*args, **kwargs)\n\n    async def _disconnect_reset_raise_on_watching(\n        self,\n        conn: Connection,\n        error: Exception,\n        failure_count: Optional[int] = None,\n        start_time: Optional[float] = None,\n        command_name: Optional[str] = None,","sourceCodeStart":1879,"sourceCodeEnd":1915,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/client.py#L1879-L1915","documentation":"Raised by Pipeline.multi() (redis/asyncio/client.py:1897) when self.command_stack is non-empty. The explicit-transaction contract requires WATCH (optional) then multi() then queued commands; queuing commands before multi() means they were sent as immediate commands, not part of a transaction. multi() refuses to start a transaction over already-issued commands.","triggerScenarios":"Calling `pipe.set('k', 1)` (or any command) before `pipe.multi()`. In the explicit-transaction pattern the only commands allowed before multi() are WATCH; anything else populates command_stack and trips this guard.","commonSituations":"Migrating from auto-transaction pipelines to explicit multi() without reordering; helper functions that append commands before the transaction is opened.","solutions":["Call multi() before queuing any non-WATCH commands.","Use `client.pipeline(transaction=True)` which opens MULTI for you, then queue commands normally.","If you must read before the transaction, do it on the base client (not the pipeline) or via WATCH + immediate_execute."],"exampleFix":"// before\npipe = r.pipeline()\nawait pipe.set('k', 'v')\npipe.multi()  # raises: commands already issued\n// after\npipe = r.pipeline(transaction=True)\nawait pipe.set('k', 'v')\nawait pipe.execute()","handlingStrategy":"validation","validationCode":"pipe = r.pipeline(transaction=True)  # MULTI opened automatically\nawait pipe.set('k', 'v')\nawait pipe.execute()","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Open the transaction (multi() or transaction=True) before queuing commands.","Only WATCH may precede multi(); do all reads via WATCH + immediate_execute or the base client.","Keep transaction construction in a single linear code block."],"tags":["redis","asyncio","pipeline","transactions","api-misuse"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}