{"record":{"id":"dc9131344e52002c","repo":"redis/redis-py","slug":"unlinking-multiple-keys-is-not-implemented-in-pipe","errorCode":null,"errorMessage":"unlinking multiple keys is not implemented in pipeline command","messagePattern":"unlinking multiple keys is not implemented in pipeline command","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"error","filePath":"redis/asyncio/cluster.py","lineNumber":3054,"sourceCode":"\n    async def watch(self, *names):\n        raise RedisClusterException(\n            \"method watch() is not supported outside of transactional context\"\n        )\n\n    async def unwatch(self):\n        raise RedisClusterException(\n            \"method unwatch() is not supported outside of transactional context\"\n        )\n\n    async def discard(self):\n        raise RedisClusterException(\n            \"method discard() is not supported outside of transactional context\"\n        )\n\n    async def unlink(self, *names):\n        if len(names) != 1:\n            raise RedisClusterException(\n                \"unlinking multiple keys is not implemented in pipeline command\"\n            )\n\n        return self.execute_command(\"UNLINK\", names[0])\n\n\nclass TransactionStrategy(AbstractStrategy):\n    NO_SLOTS_COMMANDS = {\"UNWATCH\"}\n    IMMEDIATE_EXECUTE_COMMANDS = {\"WATCH\", \"UNWATCH\"}\n    UNWATCH_COMMANDS = {\"DISCARD\", \"EXEC\", \"UNWATCH\"}\n    SLOT_REDIRECT_ERRORS = (AskError, MovedError)\n    CONNECTION_ERRORS = (\n        ConnectionError,\n        OSError,\n        ClusterDownError,\n        SlotNotCoveredError,\n    )\n","sourceCodeStart":3036,"sourceCodeEnd":3072,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/cluster.py#L3036-L3072","documentation":"Thrown by the async ClusterPipeline non-atomic strategy's unlink() override (redis/asyncio/cluster.py:3052). In cluster mode a multi-key UNLINK cannot be routed to a single node because the keys may map to different hash slots, so only the single-key form is implemented inside a pipeline. Passing more than one key therefore fails fast rather than silently corrupting the pipeline. Use per-key calls or the non-pipeline client which fans out across slots.","triggerScenarios":"Calling `pipeline.unlink('k1', 'k2')` (or any variadic call with len(names) != 1) on an async redis.asyncio.RedisCluster pipeline using the default non-atomic execution strategy.","commonSituations":"Migrating a standalone Redis pipeline that batch-unlinks several keys into cluster mode; refactoring cleanup code that used `unlink(*keys_list)`.","solutions":["Call unlink once per key: `for k in keys: await pipeline.unlink(k)`","Use the regular cluster client `await client.unlink(*keys)` which fans out across slots, instead of the pipeline","If all keys share a hash slot (hash tag), the non-atomic pipeline still requires single-key calls - loop over them"],"exampleFix":"// before\nawait pipeline.unlink('k1', 'k2', 'k3')\n// after\nfor k in ('k1', 'k2', 'k3'):\n    await pipeline.unlink(k)","handlingStrategy":"validation","validationCode":"if len(keys) > 1:\n    # cluster pipeline unlink supports only one key at a time\n    for k in keys:\n        await pipeline.unlink(k)\nelse:\n    await pipeline.unlink(keys[0])","typeGuard":null,"tryCatchPattern":"try:\n    await pipeline.unlink(*keys)\nexcept RedisClusterException as e:\n    if 'unlinking multiple keys' in str(e):\n        for k in keys:\n            await pipeline.unlink(k)\n    else:\n        raise","preventionTips":["Never pass more than one key to ClusterPipeline.unlink","For batch deletes across slots use the client.unlink() fan-out, not the pipeline"],"tags":["cluster","pipeline","async","unlink","cross-slot"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}