redis/redis-py · error · RedisClusterException
unlinking multiple keys is not implemented in pipeline comma
Error message
unlinking multiple keys is not implemented in pipeline command
What it means
Raised by PipelineStrategy.unlink() when more than one key is passed. Cluster pipelines route each command to a single node; UNLINK with multiple cross-slot keys cannot be placed on one shard, and the cluster pipeline does not auto-split multi-key UNLINK the way standalone Redis does. To avoid a CROSSSLOT failure the library limits pipeline unlink to exactly one key.
Source
Thrown at redis/asyncio/cluster.py:3053
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"
)
return self.execute_command("UNLINK", names[0])
class TransactionStrategy(AbstractStrategy):
NO_SLOTS_COMMANDS = {"UNWATCH"}
IMMEDIATE_EXECUTE_COMMANDS = {"WATCH", "UNWATCH"}
UNWATCH_COMMANDS = {"DISCARD", "EXEC", "UNWATCH"}
SLOT_REDIRECT_ERRORS = (AskError, MovedError)
CONNECTION_ERRORS = (
ConnectionError,
OSError,
ClusterDownError,
SlotNotCoveredError,
)
View on GitHub (pinned to da03cdc7e8)
Solutions
- Queue one unlink per key: for k in keys: pipe.unlink(k). The pipeline will batch them per shard automatically.
- Use a single-key UNLINK if all keys share a hash tag and you concat them — but the pipeline API still requires one key per call, so loop.
- For bulk cross-cluster delete outside a pipeline, call rc.delete(*keys) which the client fans out per slot.
Example fix
// before
pipe = rc.pipeline()
pipe.unlink('k1', 'k2', 'k3') # raises
await pipe.execute()
// after
pipe = rc.pipeline()
for k in ['k1', 'k2', 'k3']:
pipe.unlink(k)
await pipe.execute() Defensive patterns
Strategy: validation
Validate before calling
def unlink_many_pipeline(pipe, keys):
for k in keys:
pipe.unlink(k) # one key per command; pipeline batches per shard
return pipe
# usage
pipe = rc.pipeline()
unlink_many_pipeline(pipe, ['k1', 'k2', 'k3'])
await pipe.execute() Type guard
from typing import Iterable
def is_single_key(names) -> bool:
if isinstance(names, str):
return True
if isinstance(names, Iterable):
return len(list(names)) == 1
return False Try / catch
from redis.cluster import RedisClusterException
try:
pipe.unlink('k1', 'k2', 'k3')
await pipe.execute()
except RedisClusterException as e:
if 'unlinking multiple keys' in str(e):
for k in ['k1', 'k2', 'k3']:
pipe.unlink(k)
await pipe.execute()
else:
raise Prevention
- Queue one unlink per key in cluster pipelines rather than batching keys in one call.
- For cross-cluster bulk delete outside a pipeline, use rc.delete(*keys) which fans out by slot.
- Wrap pipeline unlink helpers to enforce the single-key invariant.
When it happens
Trigger: Calling pipe.unlink('k1', 'k2') or pipe.unlink(['k1', 'k2']) in a cluster pipeline where len(names) != 1. The check at cluster.py:3052 rejects any call with zero or more-than-one keys.
Common situations: Porting standalone bulk-delete code (pipe.unlink(*many_keys)) to a cluster pipeline; deleting a batch of keys that span slots; cleanup routines that unlink variable-length key lists.
Related errors
- {command} - all keys must map to the same key slot
- shard_hint is deprecated in cluster mode
- No targets were found to execute {cmd.args} command on
- Too many targets for command {cmd.args}
- method multi() is not supported outside of transactional con
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/dc9131344e52002c.json.
Report an issue: GitHub.