redis/redis-py · error · RedisClusterException

unlinking multiple keys is not implemented in pipeline…

Error message

unlinking multiple keys is not implemented in pipeline command

What it means

Raised by PipelineStrategy.unlink() in redis/cluster.py:4576 when more than one key is passed. UNLINK is the non-blocking DEL; the same cross-slot constraint applies in a non-transactional cluster pipeline, so the method restricts to a single key.

Solutions

  1. Unlink keys one at a time: for k in keys: pipe.unlink(k).
  2. Use a transactional pipeline if keys share a hash tag and atomicity is needed.
  3. Call rc.unlink(*keys) outside the pipeline for standalone-style routing.

Example fix

// before
with rc.pipeline() as pipe:
    pipe.unlink('k1', 'k2', 'k3')
    pipe.execute()
// after
with rc.pipeline() as pipe:
    for k in ['k1', 'k2', 'k3']:
        pipe.unlink(k)
    pipe.execute()
Defensive patterns

Strategy: validation

Validate before calling

keys = ['k1', 'k2', 'k3']
with rc.pipeline() as pipe:
    for k in keys:
        pipe.unlink(k)
    pipe.execute()

Type guard

def is_single_key(names) -> bool:
    return len(names) == 1

Try / catch

from redis.exceptions import RedisClusterException
try:
    pipe.unlink(*keys)
except RedisClusterException:
    for k in keys:
        pipe.unlink(k)

Prevention

When it happens

Trigger: Calling pipe.unlink('k1', 'k2', 'k3') (multiple keys) on a non-transactional ClusterPipeline, particularly when keys hash to different slots.

Common situations: Bulk-cleanup code using UNLINK for lazy deletion, ported from standalone to cluster; passing a variable-length key list.

Related errors


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

Appendix: source

Thrown at redis/cluster.py:4576

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