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 in TransactionStrategy._validate_watch (redis/cluster.py:4651) as RedisError when WATCH is called after MULTI has already started an explicit transaction. Per Redis semantics, WATCH must be issued before MULTI; once MULTI begins, the watched-keys set is frozen until EXEC/UNWATCH.
Source
Thrown at redis/cluster.py:4651
)
self._pipeline_slots.add(slot_number)
elif args[0] not in self.NO_SLOTS_COMMANDS:
raise RedisClusterException(
f"Cannot identify slot number for command: {args[0]},"
"it cannot be triggered in a transaction"
)
return self._immediate_execute_command(*args, **kwargs)
else:
if slot_number is not None:
self._pipeline_slots.add(slot_number)
return self.pipeline_execute_command(*args, **kwargs)
def _validate_watch(self):
if self._explicit_transaction:
raise RedisError("Cannot issue a WATCH after a MULTI")
self._watching = True
def _immediate_execute_command(self, *args, **options):
return self._retry.call_with_retry(
lambda: self._get_connection_and_send_command(*args, **options),
self._reinitialize_on_error,
with_failure_count=True,
)
def _get_connection_and_send_command(self, *args, **options):
redis_node, connection = self._get_client_and_connection_for_transaction()
# Start timing for observability
start_time = time.monotonic()
try:
response = self._send_command_parse_response(View on GitHub (pinned to da03cdc7e8)
Solutions
- Issue WATCH before MULTI: pipe.watch('k'); val = pipe.get(...); pipe.multi(); pipe.set(...); pipe.execute().
- If you must change watched keys, UNWATCH (or EXEC) first, then re-WATCH before a new MULTI.
- Review the order of operations: every WATCH must precede the MULTI that opens the transaction.
Example fix
# before
pipe = rc.pipeline(transaction=True)
pipe.multi()
pipe.watch('k') # raises: Cannot issue a WATCH after a MULTI
# after
pipe = rc.pipeline(transaction=True)
pipe.watch('k') # WATCH BEFORE MULTI
pipe.multi()
pipe.set('k', 'v')
pipe.execute() Defensive patterns
Strategy: validation
Validate before calling
class TxBuilder:
def __init__(self, client):
self.pipe = client.pipeline(transaction=True)
self._multi_started = False
def watch(self, *keys):
if self._multi_started:
raise RuntimeError('WATCH after MULTI is not allowed')
self.pipe.watch(*keys)
def multi(self):
self._multi_started = True
self.pipe.multi() Try / catch
from redis.exceptions import RedisError
try:
pipe.watch('k')
except RedisError as e:
if 'WATCH after a MULTI' in str(e):
pipe.unwatch()
pipe.watch('k')
else:
raise Prevention
- Always WATCH before MULTI; never reorder.
- To re-watch mid-flow, UNWATCH (or EXEC) first, then WATCH, then MULTI again.
- Encapsulate the ordering in a small builder/helper so callers cannot get it wrong.
When it happens
Trigger: pipe.multi(); pipe.watch('k'). The _explicit_transaction flag is True when multi() ran, so the subsequent WATCH call hits _validate_watch and raises.
Common situations: Reordering optimistic-locking code so MULTI precedes WATCH. Copy-pasting transaction bodies that append WATCH calls. Race-free intent gone wrong: calling MULTI early then trying to add watches.
Related errors
- Cannot issue a WATCH after a MULTI
- method watch() is not supported outside of transactional con
- method unwatch() is not supported outside of transactional c
- method watch() is not supported outside of transactional con
- At least a command with a key is needed to identify a node
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/52608c81bd5810ad.json.
Report an issue: GitHub.