{"record":{"id":"4d6731ba93fe97c0","repo":"redis/redis-py","slug":"cannot-issue-nested-calls-to-multi-4d6731","errorCode":null,"errorMessage":"Cannot issue nested calls to MULTI","messagePattern":"Cannot issue nested calls to MULTI","errorType":"exception","errorClass":"RedisError","httpStatus":null,"severity":"error","filePath":"redis/client.py","lineNumber":1889,"sourceCode":"        self.explicit_transaction = False\n\n        # we can safely return the connection to the pool here since we're\n        # sure we're no longer WATCHing anything\n        if self.connection:\n            self.connection_pool.release(self.connection)\n            self.connection = None\n\n    def close(self) -> None:\n        \"\"\"Close the pipeline\"\"\"\n        self.reset()\n\n    def multi(self) -> None:\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(self, *args, **kwargs):\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    def _disconnect_reset_raise_on_watching(\n        self,\n        conn: AbstractConnection,\n        error: Exception,\n        failure_count: Optional[int] = None,\n        start_time: Optional[float] = None,\n        command_name: Optional[str] = None,","sourceCodeStart":1871,"sourceCodeEnd":1907,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/client.py#L1871-L1907","documentation":"Raised by Pipeline.multi() when self.explicit_transaction is already True — i.e. MULTI has already been issued on this pipeline. Redis transactions cannot be nested; calling pipeline.multi() twice is a client-side programming error caught before anything is sent.","triggerScenarios":"Calling pipe.multi() twice on the same Pipeline. The first call sets explicit_transaction=True; the second hits the guard at client.py:1888.","commonSituations":"Wrapping multi() in a helper that is called more than once; building transactions dynamically with a loop that re-enters multi(); copy-paste from an example that already calls multi().","solutions":["Call pipeline.multi() exactly once per transaction; remove the duplicate call.","If constructing transactions conditionally, guard with a flag so multi() is invoked at most once.","Reset the pipeline (pipe.reset() / new pipe) before starting a new transaction."],"exampleFix":"// before\npipe = r.pipeline()\npipe.multi()\npipe.multi()  # RedisError\n\n// after\npipe = r.pipeline()\npipe.multi()\npipe.set('k', 'v')\npipe.execute()","handlingStrategy":"validation","validationCode":"if pipe.explicit_transaction:\n    raise RuntimeError('MULTI already issued on this pipeline')\npipe.multi()","typeGuard":"def pipeline_can_enter_multi(pipe) -> bool:\n    return not pipe.explicit_transaction","tryCatchPattern":"from redis.exceptions import RedisError\ntry:\n    pipe.multi()\nexcept RedisError as e:\n    if 'nested calls to MULTI' in str(e):\n        # already in a transaction; proceed to queue commands\n        pass\n    else:\n        raise","preventionTips":["Call multi() at most once per pipeline.","Encapsulate transaction construction in a helper that tracks the multi flag.","Prefer pipeline(transaction=True) for auto-MULTI so you never call multi() yourself."],"tags":["pipeline","transaction","api-misuse"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}