redis/redis-py · error · RedisError

Cannot issue nested calls to MULTI

Error message

Cannot issue nested calls to MULTI

What it means

Raised by Pipeline.multi() (redis/asyncio/client.py:1895) when self.explicit_transaction is already True. A transactional pipeline is begun with a single multi() call and terminated by execute()/discard(); calling multi() again is a programmer error. The library raises RedisError rather than silently nesting.

Source

Thrown at redis/asyncio/client.py:1895

            # the pipeline must not be left holding a reference to a
            # connection that is being returned to the pool. Shield the
            # release itself so a second cancel cannot split the pool's
            # internal in-use/available bookkeeping mid-update.
            if self.connection:
                connection, self.connection = self.connection, None
                await asyncio.shield(self.connection_pool.release(connection))

    async def aclose(self) -> None:
        """Alias for reset(), a standard method name for cleanup"""
        await self.reset()

    def multi(self):
        """
        Start a transactional block of the pipeline after WATCH commands
        are issued. End the transactional block with `execute`.
        """
        if self.explicit_transaction:
            raise RedisError("Cannot issue nested calls to MULTI")
        if self.command_stack:
            raise RedisError(
                "Commands without an initial WATCH have already been issued"
            )
        self.explicit_transaction = True

    def execute_command(
        self, *args, **kwargs
    ) -> Union["Pipeline", Awaitable["Pipeline"]]:
        if (self.watching or args[0] == "WATCH") and not self.explicit_transaction:
            return self.immediate_execute_command(*args, **kwargs)
        return self.pipeline_execute_command(*args, **kwargs)

    async def _disconnect_reset_raise_on_watching(
        self,
        conn: Connection,
        error: Exception,
        failure_count: Optional[int] = None,

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Call multi() exactly once per transaction; create a fresh pipeline via `client.pipeline(transaction=True)` for each transaction.
  2. Let `client.pipeline(transaction=True)` auto-issue MULTI so you never call multi() manually.
  3. Track explicit_transaction state and skip redundant multi() calls.

Example fix

// before
pipe = r.pipeline()
pipe.multi()
pipe.multi()  # raises
// after
pipe = r.pipeline(transaction=True)
# MULTI issued automatically on execute; do not call multi()
Defensive patterns

Strategy: validation

Validate before calling

pipe = r.pipeline()
if getattr(pipe, 'explicit_transaction', False):
    raise RuntimeError('pipeline already in a transaction')
pipe.multi()

Prevention

When it happens

Trigger: Calling `pipe.multi()` twice on the same Pipeline object, e.g. in a loop that calls multi() per batch or in a helper that wraps multi() and is invoked twice.

Common situations: Reusing a pipeline across iterations and re-entering multi(); framework code that opens transactions conditionally and double-opens on retry.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/1777297d646b788b.json. Report an issue: GitHub.