{"id":"0d7572b06eec4ec1","repo":"redis/redis-py","slug":"invalid-args-in-command-command-args","errorCode":null,"errorMessage":"Invalid args in command: {command, *args}","messagePattern":"Invalid args in command: (.+?)","errorType":"exception","errorClass":"RedisClusterException","httpStatus":null,"severity":"error","filePath":"redis/asyncio/cluster.py","lineNumber":998,"sourceCode":"        return nodes\n\n    async def _determine_slot(self, command: str, *args: Any) -> int:\n        if self.command_flags.get(command) == SLOT_ID:\n            # The command contains the slot ID\n            return int(args[0])\n\n        # Get the keys in the command\n\n        # EVAL and EVALSHA are common enough that it's wasteful to go to the\n        # redis server to parse the keys. Besides, there is a bug in redis<7.0\n        # where `self._get_command_keys()` fails anyway. So, we special case\n        # EVAL/EVALSHA.\n        # - issue: https://github.com/redis/redis/issues/9493\n        # - fix: https://github.com/redis/redis/pull/9733\n        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}\"","sourceCodeStart":980,"sourceCodeEnd":1016,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/cluster.py#L980-L1016","documentation":"Raised inside _determine_slot() for EVAL/EVALSHA when fewer than 2 arguments follow the command name. Redis Cluster must know the script's keys to compute a hash slot for routing, and the EVAL/EVALSHA grammar requires at minimum the script body and the numkeys count. With anything less the library cannot determine where to send the command, so it rejects it client-side rather than sending a malformed request.","triggerScenarios":"rc.eval(script) with no numkeys, rc.evalsha(sha1) with no numkeys, or execute_command('EVAL', body) missing the numkeys argument. The check is len(args) < 2 where args is everything after 'EVAL'/'EVALSHA'.","commonSituations":"Building EVAL/EVALSHA argument lists dynamically and the keys list is unexpectedly empty/short; copy-paste dropping the numkeys '0' placeholder; passing a script that takes zero keys but forgetting the explicit '0' numkeys token.","solutions":["Supply both the script/SHA and numkeys: rc.eval(script, numkeys, *keys, *args).","For a keyless script pass numkeys=0 explicitly so len(args) >= 2 holds: rc.eval(script, 0).","Validate your assembled argument tuple length is >= 2 before calling execute_command('EVAL', ...)."],"exampleFix":"// before\nawait rc.evalsha(sha1)\n\n// after\nawait rc.evalsha(sha1, 0)","handlingStrategy":"validation","validationCode":"def safe_eval(rc, script, numkeys=None, keys=(), args=()):\n    # numkeys MUST be provided for cluster routing\n    if numkeys is None:\n        raise ValueError('numkeys is required for EVAL/EVALSHA on RedisCluster')\n    if int(numkeys) != len(keys):\n        raise ValueError(f'numkeys ({numkeys}) does not match number of keys ({len(keys)})')\n    return rc.eval(script, numkeys, *keys, *args)","typeGuard":"from typing import Any\n\ndef is_valid_eval_args(command: str, *args: Any) -> bool:\n    if command.upper() not in ('EVAL', 'EVALSHA'):\n        return True\n    if len(args) < 2:\n        return False\n    try:\n        int(args[1])\n    except (TypeError, ValueError):\n        return False\n    return True","tryCatchPattern":"from redis.cluster import RedisClusterException\n\ntry:\n    await rc.evalsha(sha1, numkeys, *keys)\nexcept RedisClusterException as e:\n    if 'Invalid args' in str(e) and 'EVAL' in str(e):\n        # numkeys missing — fix the call rather than retry\n        raise ValueError('EVAL/EVALSHA requires (script_or_sha, numkeys, *keys)') from e\n    raise","preventionTips":["Always pass numkeys explicitly, even when it is 0.","Prefer rc.eval()/rc.evalsha() over raw execute_command('EVAL', ...) so the signature enforces argument order.","Unit-test script-call argument assembly against len(args) >= 2 before dispatch."],"tags":["redis-cluster","lua-scripting","eval","validation"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}