redis/redis-py · error · RedisClusterException
method watch() is not supported outside of transactional con
Error message
method watch() is not supported outside of transactional context
What it means
Raised unconditionally by PipelineStrategy.watch() — the non-transactional cluster pipeline strategy. WATCH is a Redis optimistic-locking primitive that only makes sense inside a MULTI/EXEC transaction; a non-transactional cluster pipeline has no transactional context, so watch() is not supported there.
Source
Thrown at redis/asyncio/cluster.py:3037
if type(cmd.result) in RedisCluster.ERRORS_ALLOW_RETRY:
client.replace_default_node()
break
return [cmd.result for cmd in stack]
async def reset(self):
"""
Reset back to empty pipeline.
"""
self._command_queue = []
def multi(self):
raise RedisClusterException(
"method multi() is not supported outside of transactional context"
)
async def watch(self, *names):
raise RedisClusterException(
"method watch() is not supported outside of transactional context"
)
async def unwatch(self):
raise RedisClusterException(
"method unwatch() is not supported outside of transactional context"
)
async def discard(self):
raise RedisClusterException(
"method discard() is not supported outside of transactional context"
)
async def unlink(self, *names):
if len(names) != 1:
raise RedisClusterException(
"unlinking multiple keys is not implemented in pipeline command"
)View on GitHub (pinned to da03cdc7e8)
Solutions
- Use a transactional pipeline: pipe = rc.pipeline(transaction=True), then await pipe.watch('key').
- If optimistic locking isn't needed, remove the watch() call.
- Prefer the cluster lock (redis.lock.Lock) over WATCH/MULTI for distributed mutual exclusion.
Example fix
// before
pipe = rc.pipeline()
await pipe.watch('counter') # raises
// after
pipe = rc.pipeline(transaction=True)
await pipe.watch('counter') Defensive patterns
Strategy: validation
Validate before calling
def watch_if_transactional(pipe, *keys):
if not getattr(pipe, '_transaction', False):
raise ValueError('watch() requires rc.pipeline(transaction=True)')
return pipe.watch(*keys) Type guard
from redis.asyncio.cluster import TransactionStrategy
def supports_watch(pipe) -> bool:
return isinstance(getattr(pipe, '_strategy', None), TransactionStrategy) Try / catch
from redis.cluster import RedisClusterException
try:
await pipe.watch('k')
except RedisClusterException as e:
if 'watch()' in str(e):
pipe = rc.pipeline(transaction=True)
await pipe.watch('k')
else:
raise Prevention
- Use rc.pipeline(transaction=True) for any WATCH-based optimistic locking.
- Consider redis.lock.Lock for distributed locking instead of WATCH/MULTI in cluster mode.
- Keep transactional pipeline helpers separate from fire-and-forget pipelines.
When it happens
Trigger: Calling await pipe.watch('key') on a pipeline created via rc.pipeline() or rc.pipeline(transaction=False). Transactional pipelines (rc.pipeline(transaction=True)) support WATCH.
Common situations: Porting standalone optimistic-locking code to cluster mode without enabling the transactional pipeline; calling watch() for the first time on a freshly created non-transactional pipe.
Related errors
- Watched variable changed.
- method multi() is not supported outside of transactional con
- method unwatch() is not supported outside of transactional c
- method discard() is not supported outside of transactional c
- Cannot issue a WATCH after a MULTI
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/ead0dfefabaa8fbb.json.
Report an issue: GitHub.