redis/redis-py · error · RedisClusterException

method discard() is not supported outside of transactional c

Error message

method discard() is not supported outside of transactional context

What it means

PipelineStrategy.discard() (redis/cluster.py:4529) always raises. DISCARD aborts a MULTI transaction; on a non-transactional pipeline (transaction=False) there is no transaction to abort, so the call is invalid. The strategy is selected at ClusterPipeline.__init__ (redis/cluster.py:3598).

Source

Thrown at redis/cluster.py:4530

        elif request_policy == RequestPolicy.MULTI_SHARD:
            nodes = policy_callback(*args, **kwargs)
        elif request_policy == RequestPolicy.DEFAULT_KEYLESS:
            nodes = policy_callback(args[0])
        else:
            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"
            )

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Guard discard() behind the transaction mode, or only call it on a transactional pipeline.
  2. For a non-transactional pipeline, clear queued commands with pipe.reset() instead of discard().
  3. Use rc.pipeline() (default transaction=True) if you need MULTI/DISCARD semantics.

Example fix

# before
pipe = rc.pipeline(transaction=False)
try:
    pipe.discard()  # raises

# after
pipe = rc.pipeline(transaction=False)
pipe.reset()  # safe way to clear a non-transactional pipeline
Defensive patterns

Strategy: validation

Validate before calling

def safe_discard(pipe):
    # discard() is only valid on a transactional pipeline
    if not getattr(pipe, '_execution_strategy', None).__class__.__name__ == 'TransactionStrategy':
        pipe.reset()  # non-transactional: just clear the queue
        return
    pipe.discard()

Try / catch

from redis.exceptions import RedisClusterException

try:
    pipe.discard()
except RedisClusterException:
    pipe.reset()

Prevention

When it happens

Trigger: Calling pipe.discard() on a pipeline created with rc.pipeline(transaction=False).

Common situations: Reusable error-handling code that calls discard() to clean up a pipeline, run against a non-transactional pipeline. Cleanup/finally blocks that blindly discard. Migrating standalone patterns that use DISCARD defensively.

Related errors


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