{"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":"validation","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/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/commands/core.py#L842-L878","documentation":"Raised by client_list when the client_id argument is not a Python list. The default is an empty list [] and the method appends b'ID' then spreads the list entries into args, so a scalar or tuple would break the protocol encoding.","triggerScenarios":"Calling r.client_list(client_id=42) with a single int. Passing a tuple client_id=(1,2,3) or a generator/iterator. Passing a numpy array or other non-list sequence.","commonSituations":"Developer wraps a single ID without brackets: client_id=some_id instead of client_id=[some_id]. Data loaded from a DB cursor as a tuple. Passing the result of client_id() (a scalar) directly.","solutions":["Wrap single IDs in a list: client_list(client_id=[my_id]).","Convert tuples/other sequences: client_list(client_id=list(my_ids)).","Ensure the value is a list before passing, especially when sourced dynamically."],"exampleFix":"# before\nr.client_list(client_id=current_id)\n# after\nr.client_list(client_id=[current_id])","handlingStrategy":"type-guard","validationCode":"if not isinstance(client_id, list):\n    client_id = [client_id] if client_id is not None else []\n# now safe","typeGuard":"def is_client_id_list(v) -> bool:\n    return isinstance(v, list)","tryCatchPattern":null,"preventionTips":["Even a single ID must be passed inside a list for client_list.","Normalize non-list sequences to list() at the boundary of your application."],"tags":["client-management","validation","type-error","dataerror"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}