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

  1. Issue all WATCH calls before multi(): `await pipe.watch('k'); pipe.multi()`.
  2. Use `client.pipeline(transaction=True)` and call watch() before queuing commands (watch() runs immediately when not in explicit_transaction).
  3. 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

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


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