{"record":{"id":"6a9fd3acaf114748","repo":"redis/redis-py","slug":"client-id-must-be-a-list","errorCode":null,"errorMessage":"client_id must be a list","messagePattern":"client_id must be a list","errorType":"exception","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":860,"sourceCode":"        \"\"\"\n        Returns a list of currently connected clients.\n        If type of client specified, only that type will be returned.\n\n        :param _type: optional. one of the client types (normal, master,\n         replica, pubsub)\n        :param client_id: optional. a list of client ids\n\n        For more information, see https://redis.io/commands/client-list\n        \"\"\"\n        args = []\n        if _type is not None:\n            client_types = (\"normal\", \"master\", \"replica\", \"pubsub\")\n            if str(_type).lower() not in client_types:\n                raise DataError(f\"CLIENT LIST _type must be one of {client_types!r}\")\n            args.append(b\"TYPE\")\n            args.append(_type)\n        if not isinstance(client_id, list):\n            raise DataError(\"client_id must be a list\")\n        if client_id:\n            args.append(b\"ID\")\n            args += client_id\n        return self.execute_command(\"CLIENT LIST\", *args, **kwargs)\n\n    @overload\n    def client_getname(self: SyncClientProtocol, **kwargs) -> bytes | str | None: ...\n\n    @overload\n    def client_getname(\n        self: AsyncClientProtocol, **kwargs\n    ) -> Awaitable[bytes | str | None]: ...\n\n    def client_getname(self, **kwargs) -> (bytes | str | None) | Awaitable[\n        bytes | str | None\n    ]:\n        \"\"\"\n        Returns the current connection name","sourceCodeStart":842,"sourceCodeEnd":878,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/commands/core.py#L842-L878","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap a single id in a list: r.client_list(client_id=[42])","Convert other iterables: r.client_list(client_id=list(ids))","If using numpy/pandas, call .tolist() first"],"exampleFix":"# before\nr.client_list(client_id=r.client_id())\n# after\nr.client_list(client_id=[r.client_id()])","handlingStrategy":"type-guard","validationCode":"if client_id is not None and not isinstance(client_id, list):\n    client_id = list(client_id)\nr.client_list(client_id=client_id or [])","typeGuard":"from typing import List, TypeGuard\ndef is_id_list(v) -> TypeGuard[List[int]]:\n    return isinstance(v, list) and all(isinstance(x, int) for x in v)","tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    r.client_list(client_id=ids)\nexcept DataError as e:\n    if \"client_id must be a list\" in str(e):\n        r.client_list(client_id=list(ids))\n    else:\n        raise","preventionTips":["Always pass client_id as a Python list, even for a single id.","Normalize tuples/sets/generators with list(...) at the boundary.","If ids originate from numpy/pandas, call .tolist()."],"tags":["client-management","validation","dataerror","type-coercion"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}