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 TransactionStrategy._validate_watch (redis/cluster.py:4704) when WATCH is called after MULTI has already started the transaction. Per Redis semantics WATCH inside a MULTI is illegal (Redis returns an error); the cluster client enforces this client-side. Once _explicit_transaction is True, no further WATCH is allowed.
Solutions
- Always call watch() before multi()/transactional queuing begins.
- Restructure so all watched keys are known up front, then start MULTI.
- If conditions change mid-transaction, discard and restart: DISCARD, re-WATCH, re-MULTI.
Example fix
// before
with rc.pipeline(transaction=True) as pipe:
pipe.multi()
pipe.watch('k')
pipe.set('k', 'v')
// after
with rc.pipeline(transaction=True) as pipe:
pipe.watch('k')
pipe.multi()
pipe.set('k', 'v') Defensive patterns
Strategy: validation
Validate before calling
with rc.pipeline(transaction=True) as pipe:
pipe.watch('k') # WATCH BEFORE MULTI
pipe.multi()
pipe.set('k', 'v') Type guard
def watch_before_multi(sequence) -> bool:
return sequence.index('watch') < sequence.index('multi') Try / catch
from redis.exceptions import RedisError
try:
pipe.watch('k')
except RedisError as e:
if 'Cannot issue a WATCH after a MULTI' in str(e):
pipe.discard()
pipe.watch('k')
pipe.multi() Prevention
- Always WATCH before MULTI.
- Restart (DISCARD -> WATCH -> MULTI) if conditions change mid-transaction.
When it happens
Trigger: In a transactional pipeline, calling pipe.multi() (or queuing commands that implicitly start MULTI) and then calling pipe.watch(key) afterward.
Common situations: Reordering WATCH/MULTI when porting standalone code; conditional logic that adds a watch late; misunderstanding that WATCH must precede MULTI.
Related errors
- method watch() is not supported outside of transactional…
- Cannot watch or send commands on different slots
- method discard() is not supported outside of transactional…
- method multi() is not supported outside of transactional…
- method unwatch() is not supported outside of transactional…
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/52608c81bd5810ad.
Report an issue: GitHub.
Appendix: source
Thrown at redis/cluster.py:4704
)
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 6a6b581b48)