redis/redis-py · error · RedisError
Cannot issue a WATCH after a MULTI
Error message
Cannot issue a WATCH after a MULTI
What it means
Raised by Pipeline.watch() (redis/asyncio/client.py:2300) when self.explicit_transaction is True. Redis semantics forbid WATCH after MULTI has started; once a transaction is open the watched-key set is fixed. The library enforces this client-side and raises RedisError before sending anything to the server.
Source
Thrown at redis/asyncio/client.py:2300
network_peer_port=getattr(conn, "port", None),
error_type=e,
retry_attempts=actual_retry_attempts,
is_internal=False,
)
raise
finally:
await self.reset()
async def discard(self):
"""Flushes all previously queued commands
See: https://redis.io/commands/DISCARD
"""
await self.execute_command("DISCARD")
async def watch(self, *names: KeyT):
"""Watches the values at keys ``names``"""
if self.explicit_transaction:
raise RedisError("Cannot issue a WATCH after a MULTI")
return await self.execute_command("WATCH", *names)
async def unwatch(self):
"""Unwatches all previously specified keys"""
return self.watching and await self.execute_command("UNWATCH") or True
View on GitHub (pinned to da03cdc7e8)
Solutions
- Issue all WATCH calls before multi(): `await pipe.watch('k'); pipe.multi()`.
- Use `client.pipeline(transaction=True)` and call watch() before queuing commands (watch() runs immediately when not in explicit_transaction).
- If you must change the watch set, DISCARD and start a new transaction.
Example fix
// before
pipe = r.pipeline()
pipe.multi()
await pipe.watch('k') # raises
// after
pipe = r.pipeline()
await pipe.watch('k')
pipe.multi()
await pipe.set('k', 'v')
await pipe.execute() Defensive patterns
Strategy: validation
Validate before calling
pipe = r.pipeline()
await pipe.watch('k') # WATCH before MULTI
if getattr(pipe, 'explicit_transaction', False):
raise RuntimeError('cannot WATCH after MULTI')
pipe.multi()
await pipe.set('k', 'v')
await pipe.execute() Prevention
- Always WATCH before multi().
- If the watch set must change, DISCARD and start over.
- Use client.pipeline(transaction=True) and call watch() first.
When it happens
Trigger: Calling `pipe.multi()` and then `await pipe.watch('k')` on the same pipeline. The watch() guard checks explicit_transaction and aborts.
Common situations: Reordering transaction setup so WATCH lands after MULTI; helper code that conditionally adds watches inside an already-open transaction.
Related errors
- Cannot issue nested calls to MULTI
- Commands without an initial WATCH have already been issued
- 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/deba33a2b31e3936.json.
Report an issue: GitHub.