redis/redis-py · error · RedisError
Commands without an initial WATCH have already been issued
Error message
Commands without an initial WATCH have already been issued
What it means
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.
Source
Thrown at redis/asyncio/client.py:1897
# 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,
start_time: Optional[float] = None,
command_name: Optional[str] = None,View on GitHub (pinned to da03cdc7e8)
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.
Example fix
// before
pipe = r.pipeline()
await pipe.set('k', 'v')
pipe.multi() # raises: commands already issued
// after
pipe = r.pipeline(transaction=True)
await pipe.set('k', 'v')
await pipe.execute() Defensive patterns
Strategy: validation
Validate before calling
pipe = r.pipeline(transaction=True) # MULTI opened automatically
await pipe.set('k', 'v')
await pipe.execute() Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Migrating from auto-transaction pipelines to explicit multi() without reordering; helper functions that append commands before the transaction is opened.
Related errors
- Cannot issue nested calls to MULTI
- Cannot issue a WATCH after a MULTI
- A {type(error).__name__} occurred while watching one or more
- Watched variable changed.
- Wrong number of response items from pipeline execution
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/92ddfc6fce316a8b.json.
Report an issue: GitHub.