redis/redis-py · error · RedisClusterException
method multi() is not supported outside of transactional con
Error message
method multi() is not supported outside of transactional context
What it means
PipelineStrategy.multi() (redis/cluster.py:4524) unconditionally raises because MULTI only makes sense inside a transactional pipeline. The execution strategy is chosen in ClusterPipeline.__init__ (redis/cluster.py:3598): PipelineStrategy is used when transaction=False, TransactionStrategy when transaction=True. Calling multi() on a non-transactional pipeline is a contradiction.
Source
Thrown at redis/cluster.py:4525
policy_callback = self._pipe._policies_callback_mapping[request_policy]
if request_policy == RequestPolicy.DEFAULT_KEYED:
nodes = policy_callback(command, *args)
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"
)
View on GitHub (pinned to da03cdc7e8)
Solutions
- Use a transactional pipeline: rc.pipeline(transaction=True) (the default) before calling pipe.multi().
- If you intentionally use transaction=False, remove all multi()/discard()/watch()/unwatch() calls from that code path.
- Branch on the pipeline's mode if shared code is unavoidable: only call multi() when transaction is enabled.
Example fix
# before
pipe = rc.pipeline(transaction=False)
pipe.multi() # raises
# after
pipe = rc.pipeline(transaction=True)
pipe.multi()
pipe.set('k', 'v')
pipe.execute() Defensive patterns
Strategy: validation
Validate before calling
def open_transactional_pipeline(client, transaction=True):
pipe = client.pipeline(transaction=transaction)
if not transaction:
# multi() is illegal here; make that explicit at construction time
object.__setattr__(pipe, '_no_multi', True)
return pipe
def safe_multi(pipe):
if getattr(pipe, '_no_multi', False):
raise RuntimeError('multi() not allowed on a non-transactional pipeline')
pipe.multi() Try / catch
from redis.exceptions import RedisClusterException
try:
pipe.multi()
except RedisClusterException:
pipe = rc.pipeline(transaction=True)
pipe.multi() Prevention
- Use the default transaction=True whenever you need MULTI/DISCARD/WATCH.
- Keep transactional and non-transactional pipeline code paths separate.
- Do not call multi() in shared helpers that may receive a transaction=False pipeline.
When it happens
Trigger: Creating rc.pipeline(transaction=False) (or rc.pipeline() with a False transaction argument) and then calling pipe.multi(). The non-transactional strategy rejects it.
Common situations: Switching a pipeline to transaction=False to avoid EXEC overhead, then leaving MULTI/WATCH calls in place. Code paths shared between transactional and non-transactional pipelines. Misreading the standalone pipeline API where multi() can sometimes be called late.
Related errors
- method discard() is not supported outside of transactional c
- At least a command with a key is needed to identify a node
- Cannot watch or send commands on different slots
- Cannot identify slot number for command: {args[0]},it cannot
- Cannot issue a WATCH after a MULTI
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/402d3db6aefbffb7.json.
Report an issue: GitHub.