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

Raised by AbstractStrategy.watch() in redis/cluster.py:4557. WATCH is a conditional-transaction primitive that must be sent on the connection owning the watched key's slot; on a non-transactional cluster pipeline there is no pinned connection, so watch() is rejected. Only the TransactionStrategy supports WATCH (via _validate_watch / immediate execution).

Solutions

  1. Use a transactional pipeline: with rc.pipeline(transaction=True) as pipe: pipe.watch('k'); pipe.multi(); ...
  2. Ensure all watched keys hash to the same slot (use hash tags {tag}key1, {tag}key2).
  3. If you only need conditional execution without atomicity, reconsider whether WATCH is necessary.

Example fix

// before
pipe = rc.pipeline()
pipe.watch('k')
// after
with rc.pipeline(transaction=True) as pipe:
    pipe.watch('k')
    val = pipe.get('k').execute()[0]
    pipe.multi()
    pipe.set('k', newval)
    pipe.execute()
Defensive patterns

Strategy: validation

Validate before calling

with rc.pipeline(transaction=True) as pipe:
    pipe.watch('k')  # OK in transactional pipeline

Type guard

def is_transactional_pipe(pipe) -> bool:
    return bool(getattr(pipe, 'transaction', False))

Try / catch

from redis.exceptions import RedisClusterException
try:
    pipe.watch('k')
except RedisClusterException:
    with rc.pipeline(transaction=True) as tpipe:
        tpipe.watch('k')

Prevention

When it happens

Trigger: Calling pipe.watch('key') on a ClusterPipeline opened without transaction=True, or calling watch() before the transactional context is established.

Common situations: Porting optimistic-locking code (WATCH/MULTI/EXEC) from standalone Redis to cluster; assuming watch works on the default pipeline.

Related errors


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

Appendix: source

Thrown at redis/cluster.py:4557

            nodes = policy_callback()

        if args[0].lower() == "ft.aggregate":
            self._aggregate_nodes = nodes

        return nodes

    def multi(self):
        raise RedisClusterException(
            "method multi() is not supported outside of transactional context"
        )

    def discard(self):
        raise RedisClusterException(
            "method discard() is not supported outside of transactional context"
        )

    def watch(self, *names):
        raise RedisClusterException(
            "method watch() is not supported outside of transactional context"
        )

    def unwatch(self, *names):
        raise RedisClusterException(
            "method unwatch() is not supported outside of transactional context"
        )

    def delete(self, *names):
        if len(names) != 1:
            raise RedisClusterException(
                "deleting multiple keys is not implemented in pipeline command"
            )

        return self.execute_command("DEL", names[0])

    def unlink(self, *names):
        if len(names) != 1:

View on GitHub (pinned to 6a6b581b48)