redis/redis-py · error · RedisClusterException
Invalid args in command
Error message
Invalid args in command: {command, *args} What it means
Raised in _determine_slot() for EVAL/EVALSHA when the command has fewer than 2 positional args after the command name. The cluster client needs at least the script body (args[0]) and the numkeys count (args[1]) to extract the keys and compute their slot, so it rejects malformed scripting calls early instead of producing a confusing downstream error.
Solutions
- Provide at least (script, numkeys) plus numkeys key arguments: client.eval(script, numkeys, *keys_and_args).
- If running a parameterless script on the cluster, pass numkeys=0 so the client routes to a random slot.
- Double-check wrapper/adapter code that forwards *args to eval() to ensure nothing is being dropped.
Example fix
// before
await rc.eval("return 1")
// after
await rc.eval("return 1", 0) Defensive patterns
Strategy: validation
Validate before calling
async def safe_eval(rc, script, numkeys, *keys_and_args):
if len([script, numkeys, *keys_and_args]) < 2:
raise ValueError('eval requires at least (script, numkeys)')
return await rc.eval(script, numkeys, *keys_and_args) Type guard
null
Try / catch
from redis.exceptions import RedisClusterException
try:
await rc.eval(script, *args)
except RedisClusterException as e:
if 'Invalid args in command' in str(e):
# fix args and retry with explicit numkeys
await rc.eval(script, 0) Prevention
- Always pass numkeys as the second positional arg to eval/evalsha.
- Prefer the high-level client.eval(script, numkeys, *keys) signature over execute_command('EVAL', ...).
- Validate *args length in wrapper functions before forwarding.
When it happens
Trigger: Calling client.eval() or client.evalsha() with no args, only a script, or only script+numkeys without the actual key arguments such that len(args) < 2. Also triggered by manually building the command via execute_command('EVAL', script) without numkeys.
Common situations: Mistakenly passing a pre-joined argument string, forgetting the numkeys parameter, or a wrapper function that drops positional arguments when forwarding to eval().
Related errors
- At least a command with a key is needed to identify a node
- Cannot execute FT.CURSOR commands without FT.AGGREGATE
- Cannot identify slot number for command
- command doesn't exist in Redis commands
- Invalid args in command
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/0d7572b06eec4ec1.
Report an issue: GitHub.
Appendix: source
Thrown at redis/asyncio/cluster.py:999
return nodes
async def _determine_slot(self, command: str, *args: Any) -> int:
if self.command_flags.get(command) == SLOT_ID:
# The command contains the slot ID
return int(args[0])
# Get the keys in the command
# EVAL and EVALSHA are common enough that it's wasteful to go to the
# redis server to parse the keys. Besides, there is a bug in redis<7.0
# where `self._get_command_keys()` fails anyway. So, we special case
# EVAL/EVALSHA.
# - issue: https://github.com/redis/redis/issues/9493
# - fix: https://github.com/redis/redis/pull/9733
if command.upper() in ("EVAL", "EVALSHA"):
# command syntax: EVAL "script body" num_keys ...
if len(args) < 2:
raise RedisClusterException(
f"Invalid args in command: {command, *args}"
)
keys = args[2 : 2 + int(args[1])]
# if there are 0 keys, that means the script can be run on any node
# so we can just return a random slot
if not keys:
return random.randrange(0, REDIS_CLUSTER_HASH_SLOTS)
else:
keys = await self.commands_parser.get_keys(command, *args)
if not keys:
# FCALL can call a function with 0 keys, that means the function
# can be run on any node so we can just return a random slot
if command.upper() in ("FCALL", "FCALL_RO"):
return random.randrange(0, REDIS_CLUSTER_HASH_SLOTS)
raise RedisClusterException(
"No way to dispatch this command to Redis Cluster. "
"Missing key.\nYou can execute the command by specifying "
f"target nodes.\nCommand: {args}"View on GitHub (pinned to 6a6b581b48)