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

  1. Supply both the script/SHA and numkeys: rc.eval(script, numkeys, *keys, *args).
  2. For a keyless script pass numkeys=0 explicitly so len(args) >= 2 holds: rc.eval(script, 0).
  3. 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

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


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/0d7572b06eec4ec1.json. Report an issue: GitHub.