{"id":"402d3db6aefbffb7","repo":"redis/redis-py","slug":"method-multi-is-not-supported-outside-of-transac-402d3d","errorCode":null,"errorMessage":"method multi() is not supported outside of transactional context","messagePattern":"method multi\\(\\) is not supported outside of transactional context","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"error","filePath":"redis/cluster.py","lineNumber":4525,"sourceCode":"\n        policy_callback = self._pipe._policies_callback_mapping[request_policy]\n\n        if request_policy == RequestPolicy.DEFAULT_KEYED:\n            nodes = policy_callback(command, *args)\n        elif request_policy == RequestPolicy.MULTI_SHARD:\n            nodes = policy_callback(*args, **kwargs)\n        elif request_policy == RequestPolicy.DEFAULT_KEYLESS:\n            nodes = policy_callback(args[0])\n        else:\n            nodes = policy_callback()\n\n        if args[0].lower() == \"ft.aggregate\":\n            self._aggregate_nodes = nodes\n\n        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","sourceCodeStart":4507,"sourceCodeEnd":4543,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/cluster.py#L4507-L4543","documentation":"PipelineStrategy.multi() (redis/cluster.py:4524) unconditionally raises because MULTI only makes sense inside a transactional pipeline. The execution strategy is chosen in ClusterPipeline.__init__ (redis/cluster.py:3598): PipelineStrategy is used when transaction=False, TransactionStrategy when transaction=True. Calling multi() on a non-transactional pipeline is a contradiction.","triggerScenarios":"Creating rc.pipeline(transaction=False) (or rc.pipeline() with a False transaction argument) and then calling pipe.multi(). The non-transactional strategy rejects it.","commonSituations":"Switching a pipeline to transaction=False to avoid EXEC overhead, then leaving MULTI/WATCH calls in place. Code paths shared between transactional and non-transactional pipelines. Misreading the standalone pipeline API where multi() can sometimes be called late.","solutions":["Use a transactional pipeline: rc.pipeline(transaction=True) (the default) before calling pipe.multi().","If you intentionally use transaction=False, remove all multi()/discard()/watch()/unwatch() calls from that code path.","Branch on the pipeline's mode if shared code is unavoidable: only call multi() when transaction is enabled."],"exampleFix":"# before\npipe = rc.pipeline(transaction=False)\npipe.multi()  # raises\n\n# after\npipe = rc.pipeline(transaction=True)\npipe.multi()\npipe.set('k', 'v')\npipe.execute()","handlingStrategy":"validation","validationCode":"def open_transactional_pipeline(client, transaction=True):\n    pipe = client.pipeline(transaction=transaction)\n    if not transaction:\n        # multi() is illegal here; make that explicit at construction time\n        object.__setattr__(pipe, '_no_multi', True)\n    return pipe\n\ndef safe_multi(pipe):\n    if getattr(pipe, '_no_multi', False):\n        raise RuntimeError('multi() not allowed on a non-transactional pipeline')\n    pipe.multi()","typeGuard":null,"tryCatchPattern":"from redis.exceptions import RedisClusterException\n\ntry:\n    pipe.multi()\nexcept RedisClusterException:\n    pipe = rc.pipeline(transaction=True)\n    pipe.multi()","preventionTips":["Use the default transaction=True whenever you need MULTI/DISCARD/WATCH.","Keep transactional and non-transactional pipeline code paths separate.","Do not call multi() in shared helpers that may receive a transaction=False pipeline."],"tags":["cluster","pipeline","transaction","api-misuse"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}