{"id":"e8fd64e8af38d29b","repo":"redis/redis-py","slug":"no-way-to-dispatch-this-command-to-redis-cluster","errorCode":null,"errorMessage":"No way to dispatch this command to Redis Cluster. Missing key.\nYou can execute the command by specifying target nodes.\nCommand: {args}","messagePattern":"No way to dispatch this command to Redis Cluster\\. Missing key\\.\nYou can execute the command by specifying target nodes\\.\nCommand: (.+?)","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"error","filePath":"redis/asyncio/cluster.py","lineNumber":1013,"sourceCode":"        if command.upper() in (\"EVAL\", \"EVALSHA\"):\n            # command syntax: EVAL \"script body\" num_keys ...\n            if len(args) < 2:\n                raise RedisClusterException(\n                    f\"Invalid args in command: {command, *args}\"\n                )\n            keys = args[2 : 2 + int(args[1])]\n            # if there are 0 keys, that means the script can be run on any node\n            # so we can just return a random slot\n            if not keys:\n                return random.randrange(0, REDIS_CLUSTER_HASH_SLOTS)\n        else:\n            keys = await self.commands_parser.get_keys(command, *args)\n            if not keys:\n                # FCALL can call a function with 0 keys, that means the function\n                #  can be run on any node so we can just return a random slot\n                if command.upper() in (\"FCALL\", \"FCALL_RO\"):\n                    return random.randrange(0, REDIS_CLUSTER_HASH_SLOTS)\n                raise RedisClusterException(\n                    \"No way to dispatch this command to Redis Cluster. \"\n                    \"Missing key.\\nYou can execute the command by specifying \"\n                    f\"target nodes.\\nCommand: {args}\"\n                )\n\n        # single key command\n        if len(keys) == 1:\n            return self.keyslot(keys[0])\n\n        # multi-key command; we need to make sure all keys are mapped to\n        # the same slot\n        slots = {self.keyslot(key) for key in keys}\n        if len(slots) != 1:\n            raise RedisClusterException(\n                f\"{command} - all keys must map to the same key slot\"\n            )\n\n        return slots.pop()","sourceCodeStart":995,"sourceCodeEnd":1031,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/cluster.py#L995-L1031","documentation":"Raised by _determine_slot() when a command yields zero keys (per COMMAND INFO key extraction) and is not FCALL/FCALL_RO. Redis Cluster routes by key hash slot, so a keyless command has no determinable destination. The library will not pick a random shard for arbitrary commands; the caller must name the target explicitly.","triggerScenarios":"Calling rc.execute_command('INFO'), rc.execute_command('DBSIZE'), rc.config_get(), rc.flushdb() etc. without target_nodes, where the command's request policy is key-based (DEFAULT_KEYED) and no key argument is present. Also triggered by custom/unknown commands the COMMAND parser cannot extract keys from.","commonSituations":"Porting standalone-Redis code (rc.info(), rc.dbsize()) to RedisCluster without adding target_nodes; issuing admin/inspection commands that are inherently keyless; using a command the connected server doesn't know so COMMAND INFO returns no key specs.","solutions":["Pass target_nodes to name the destination: rc.execute_command('DBSIZE', target_nodes='PRIMARIES') or rc.info(target_nodes=node).","Use a node-flag constant such as RedisCluster.PRIMARIES / RANDOM / ALL_NODES for fan-out admin commands.","For a single-shard call, resolve the node via rc.get_node(host=..., port=...) and pass it as target_nodes."],"exampleFix":"// before\nawait rc.dbsize()\n\n// after\nawait rc.dbsize(target_nodes=RedisCluster.PRIMARIES)","handlingStrategy":"validation","validationCode":"from redis.asyncio.cluster import RedisCluster\n\nKEYLESS_ADMIN_COMMANDS = {'INFO', 'DBSIZE', 'FLUSHDB', 'FLUSHALL', 'CONFIG', 'CLIENT', 'CLUSTER'}\n\nasync def run_keyless(rc: RedisCluster, command: str, *args, target='PRIMARIES'):\n    if command.upper().split()[0] in KEYLESS_ADMIN_COMMANDS:\n        return await rc.execute_command(command, *args, target_nodes=target)\n    return await rc.execute_command(command, *args)","typeGuard":"from typing import Any\nfrom redis.asyncio.cluster import RedisCluster\n\ndef needs_target_nodes(rc: RedisCluster, command: str) -> bool:\n    # Crude heuristic: commands the COMMAND parser extracts 0 keys from\n    keys = rc.commands_parser  # ensure initialized\n    return command.upper() not in ('FCALL', 'FCALL_RO')","tryCatchPattern":"from redis.cluster import RedisClusterException\n\ntry:\n    await rc.execute_command('DBSIZE')\nexcept RedisClusterException as e:\n    if 'Missing key' in str(e):\n        await rc.execute_command('DBSIZE', target_nodes=RedisCluster.PRIMARIES)\n    else:\n        raise","preventionTips":["Audit standalone-Redis code ported to cluster for keyless admin commands and add target_nodes.","Default to target_nodes=RedisCluster.PRIMARIES for fan-out admin/inspection commands.","Keep a helper that wraps admin commands so routing is centralized."],"tags":["redis-cluster","routing","keyless-command","admin"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}