{"id":"98709eff0fc0ba83","repo":"redis/redis-py","slug":"xdelex-requires-at-least-one-message-id","errorCode":null,"errorMessage":"XDELEX requires at least one message ID","messagePattern":"XDELEX requires at least one message ID","errorType":"validation","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":7375,"sourceCode":"    def xdelex(\n        self: AsyncClientProtocol,\n        name: KeyT,\n        *ids: StreamIdT,\n        ref_policy: Literal[\"KEEPREF\", \"DELREF\", \"ACKED\"] = \"KEEPREF\",\n    ) -> Awaitable[int]: ...\n\n    def xdelex(\n        self,\n        name: KeyT,\n        *ids: StreamIdT,\n        ref_policy: Literal[\"KEEPREF\", \"DELREF\", \"ACKED\"] = \"KEEPREF\",\n    ) -> int | Awaitable[int]:\n        \"\"\"\n        Extended version of XDEL that provides more control over how message entries\n        are deleted concerning consumer groups.\n        \"\"\"\n        if not ids:\n            raise DataError(\"XDELEX requires at least one message ID\")\n\n        if ref_policy not in {\"KEEPREF\", \"DELREF\", \"ACKED\"}:\n            raise DataError(\"XDELEX ref_policy must be one of: KEEPREF, DELREF, ACKED\")\n\n        pieces = [name, ref_policy, \"IDS\", len(ids)]\n        pieces.extend(ids)\n        return self.execute_command(\"XDELEX\", *pieces)\n\n    @overload\n    def xgroup_create(\n        self: SyncClientProtocol,\n        name: KeyT,\n        groupname: GroupT,\n        id: StreamIdT = \"$\",\n        mkstream: bool = False,\n        entries_read: int | None = None,\n    ) -> bool: ...\n","sourceCodeStart":7357,"sourceCodeEnd":7393,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/commands/core.py#L7357-L7393","documentation":"Raised by xdelex() when no message id is supplied. XDELEX needs at least one id to delete. Check at core.py:7374: `if not ids` on the *ids vararg.","triggerScenarios":"r.xdelex('mystream') with no positional ids, or r.xdelex('mystream', ref_policy='KEEPREF') only.","commonSituations":"Building the id list from a filter that returned empty and still calling; refactoring from xdel and forgetting ids; spreading an empty tuple.","solutions":["Pass at least one id: r.xdelex(name, id1, id2, ...).","Guard: if ids: r.xdelex(name, *ids) - XDELEX on zero ids is a no-op anyway.","Remember ref_policy is keyword-only-ish (after *ids) - ids come first."],"exampleFix":"# before\nr.xdelex('s')  # no ids\n# after\nif ids:\n    r.xdelex('s', *ids, ref_policy='KEEPREF')","handlingStrategy":"validation","validationCode":"if not ids:\n    raise ValueError('xdelex needs >= 1 id')\nr.xdelex(name, *ids, ref_policy=ref_policy)","typeGuard":null,"tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    r.xdelex(name, *ids)\nexcept DataError:\n    pass  # nothing to delete","preventionTips":["Short-circuit when the id list is empty - there is nothing to delete."],"tags":["redis","streams","xdelex","validation","empty-argument"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}