redis/redis-py · error · DataError

client_id must be a list

Error message

client_id must be a list

What it means

Raised by client_list() (redis/commands/core.py:860) when the client_id argument is not a Python list. The library does isinstance(client_id, list), so a single int, a tuple, a set, or a numpy array are all rejected even if they contain valid ids. This is a redis.exceptions.DataError raised before the command is sent.

Solutions

  1. Wrap a single id in a list: r.client_list(client_id=[42])
  2. Convert other iterables: r.client_list(client_id=list(ids))
  3. If using numpy/pandas, call .tolist() first

Example fix

# before
r.client_list(client_id=r.client_id())
# after
r.client_list(client_id=[r.client_id()])
Defensive patterns

Strategy: type-guard

Validate before calling

if client_id is not None and not isinstance(client_id, list):
    client_id = list(client_id)
r.client_list(client_id=client_id or [])

Type guard

from typing import List, TypeGuard
def is_id_list(v) -> TypeGuard[List[int]]:
    return isinstance(v, list) and all(isinstance(x, int) for x in v)

Try / catch

from redis.exceptions import DataError
try:
    r.client_list(client_id=ids)
except DataError as e:
    if "client_id must be a list" in str(e):
        r.client_list(client_id=list(ids))
    else:
        raise

Prevention

When it happens

Trigger: Calling r.client_list(client_id=42) (single id instead of list), r.client_list(client_id=(1,2)) (tuple), or r.client_list(client_id={1,2}) (set). The default [] does not trigger it.

Common situations: Passing a single client id obtained from client_id() directly; or passing a tuple/set from another data structure; or an ORM/pandas Series that is not a plain list.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/6a9fd3acaf114748. Report an issue: GitHub.

Appendix: source

Thrown at redis/commands/core.py:860

        """
        Returns a list of currently connected clients.
        If type of client specified, only that type will be returned.

        :param _type: optional. one of the client types (normal, master,
         replica, pubsub)
        :param client_id: optional. a list of client ids

        For more information, see https://redis.io/commands/client-list
        """
        args = []
        if _type is not None:
            client_types = ("normal", "master", "replica", "pubsub")
            if str(_type).lower() not in client_types:
                raise DataError(f"CLIENT LIST _type must be one of {client_types!r}")
            args.append(b"TYPE")
            args.append(_type)
        if not isinstance(client_id, list):
            raise DataError("client_id must be a list")
        if client_id:
            args.append(b"ID")
            args += client_id
        return self.execute_command("CLIENT LIST", *args, **kwargs)

    @overload
    def client_getname(self: SyncClientProtocol, **kwargs) -> bytes | str | None: ...

    @overload
    def client_getname(
        self: AsyncClientProtocol, **kwargs
    ) -> Awaitable[bytes | str | None]: ...

    def client_getname(self, **kwargs) -> (bytes | str | None) | Awaitable[
        bytes | str | None
    ]:
        """
        Returns the current connection name

View on GitHub (pinned to 6a6b581b48)