redis/redis-py · error · RedisClusterException
Invalid args in command: {command, *args}
Error message
Invalid args in command: {command, *args} What it means
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.
Source
Thrown at redis/asyncio/cluster.py:998
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 da03cdc7e8)
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', ...).
Example fix
// before await rc.evalsha(sha1) // after await rc.evalsha(sha1, 0)
Defensive patterns
Strategy: validation
Validate before calling
def safe_eval(rc, script, numkeys=None, keys=(), args=()):
# numkeys MUST be provided for cluster routing
if numkeys is None:
raise ValueError('numkeys is required for EVAL/EVALSHA on RedisCluster')
if int(numkeys) != len(keys):
raise ValueError(f'numkeys ({numkeys}) does not match number of keys ({len(keys)})')
return rc.eval(script, numkeys, *keys, *args) Type guard
from typing import Any
def is_valid_eval_args(command: str, *args: Any) -> bool:
if command.upper() not in ('EVAL', 'EVALSHA'):
return True
if len(args) < 2:
return False
try:
int(args[1])
except (TypeError, ValueError):
return False
return True Try / catch
from redis.cluster import RedisClusterException
try:
await rc.evalsha(sha1, numkeys, *keys)
except RedisClusterException as e:
if 'Invalid args' in str(e) and 'EVAL' in str(e):
# numkeys missing — fix the call rather than retry
raise ValueError('EVAL/EVALSHA requires (script_or_sha, numkeys, *keys)') from e
raise Prevention
- 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.
When it happens
Trigger: 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'.
Common situations: 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.
Related errors
- Subcommand {subcommand_name} not found in command {command_n
- Command {command_name} not found in commands
- Cannot execute FT.CURSOR commands without FT.AGGREGATE
- No way to dispatch this command to Redis Cluster. Missing ke
- {command} - all keys must map to the same key slot
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/0d7572b06eec4ec1.json.
Report an issue: GitHub.