redis/redis-py · error · RedisClusterException

method watch() is not supported outside of transactional…

Error message

method watch() is not supported outside of transactional context

What it means

ClusterPipeline.watch() raises unconditionally in the non-transactional PipelineStrategy. WATCH is a transaction primitive that must be issued inside a transactional context against a single node; the default non-transactional cluster pipeline cannot honour it, so the call is rejected.

Solutions

  1. Use rc.pipeline(transaction=True) which wires up WATCH through the transactional strategy.
  2. For cluster-wide optimistic locking, restrict watched keys to a single slot (shared hash tag) so a one-node WATCH is valid.
  3. If you only need batching without transactions, drop the watch() call entirely.

Example fix

// before
pipe = rc.pipeline()
await pipe.watch('account:1')  # raises

// after
pipe = rc.pipeline(transaction=True)
await pipe.watch('{acct}:1')
pipe.multi()  # supported inside transactional strategy
pipe.decrby('{acct}:1', 10)
await pipe.execute()
Defensive patterns

Strategy: validation

Validate before calling

async def cluster_watch(rc, key):
    pipe = rc.pipeline(transaction=True)
    await pipe.watch(key)  # valid only in transactional pipeline
    return pipe

Type guard

null

Try / catch

from redis.exceptions import RedisClusterException
try:
    await pipe.watch(key)
except RedisClusterException as e:
    if 'watch() is not supported' in str(e):
        pipe = rc.pipeline(transaction=True)
        await pipe.watch(key)

Prevention

When it happens

Trigger: Calling await pipe.watch('key') on a pipeline created with rc.pipeline() (transaction defaults False). The method exists on the class only to satisfy the abstract strategy interface.

Common situations: Migrating optimistic-locking code (WATCH/MULTI/EXEC) from the standalone client to the cluster client without enabling transaction mode; calling watch() on a pipeline you intend to use purely for batching.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/ead0dfefabaa8fbb. Report an issue: GitHub.

Appendix: source

Thrown at redis/asyncio/cluster.py:3038

                        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 6a6b581b48)