{"record":{"id":"31c9cc5e63e79836","repo":"redis/redis-py","slug":"unlinking-multiple-keys-is-not-implemented-in-pipe-31c9cc","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/cluster.py","lineNumber":4576,"sourceCode":"            \"method watch() is not supported outside of transactional context\"\n        )\n\n    def unwatch(self, *names):\n        raise RedisClusterException(\n            \"method unwatch() is not supported outside of transactional context\"\n        )\n\n    def delete(self, *names):\n        if len(names) != 1:\n            raise RedisClusterException(\n                \"deleting multiple keys is not implemented in pipeline command\"\n            )\n\n        return self.execute_command(\"DEL\", names[0])\n\n    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":4558,"sourceCodeEnd":4594,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/cluster.py#L4558-L4594","documentation":"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.","triggerScenarios":"Calling pipe.unlink('k1', 'k2', 'k3') (multiple keys) on a non-transactional ClusterPipeline, particularly when keys hash to different slots.","commonSituations":"Bulk-cleanup code using UNLINK for lazy deletion, ported from standalone to cluster; passing a variable-length key list.","solutions":["Unlink keys one at a time: for k in keys: pipe.unlink(k).","Use a transactional pipeline if keys share a hash tag and atomicity is needed.","Call rc.unlink(*keys) outside the pipeline for standalone-style routing."],"exampleFix":"// before\nwith rc.pipeline() as pipe:\n    pipe.unlink('k1', 'k2', 'k3')\n    pipe.execute()\n// after\nwith rc.pipeline() as pipe:\n    for k in ['k1', 'k2', 'k3']:\n        pipe.unlink(k)\n    pipe.execute()","handlingStrategy":"validation","validationCode":"keys = ['k1', 'k2', 'k3']\nwith rc.pipeline() as pipe:\n    for k in keys:\n        pipe.unlink(k)\n    pipe.execute()","typeGuard":"def is_single_key(names) -> bool:\n    return len(names) == 1","tryCatchPattern":"from redis.exceptions import RedisClusterException\ntry:\n    pipe.unlink(*keys)\nexcept RedisClusterException:\n    for k in keys:\n        pipe.unlink(k)","preventionTips":["Never multi-key unlink in a non-transactional cluster pipeline.","Loop single-key unlinks, or use rc.unlink(*keys) outside the pipeline."],"tags":["cluster","pipeline","cross-slot","unlink"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}