{"id":"2e146882fc7f0117","repo":"redis/redis-py","slug":"xnack-requires-at-least-one-message-id","errorCode":null,"errorMessage":"XNACK requires at least one message ID","messagePattern":"XNACK requires at least one message ID","errorType":"validation","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":7680,"sourceCode":"            name: name of the stream.\n            groupname: name of the consumer group.\n            mode: the nacking mode. One of SILENT, FAIL, or FATAL.\n                SILENT: consumer shutting down; decrements delivery counter.\n                FAIL: consumer unable to process; delivery counter unchanged.\n                FATAL: invalid/malicious message; delivery counter set to max.\n            *ids: one or more message IDs to NACK.\n            retrycount: optional integer >= 0. Overrides the mode's implicit\n                delivery counter adjustment with an exact value.\n            force: if True, creates a new unowned PEL entry for any ID not\n                already in the group's PEL.\n\n        Returns:\n            The number of messages successfully NACKed.\n\n        For more information, see https://redis.io/commands/xnack\n        \"\"\"\n        if not ids:\n            raise DataError(\"XNACK requires at least one message ID\")\n\n        if mode not in {\"SILENT\", \"FAIL\", \"FATAL\"}:\n            raise DataError(\"XNACK mode must be one of: SILENT, FAIL, FATAL\")\n\n        pieces: list = [name, groupname, mode, \"IDS\", len(ids)]\n        pieces.extend(ids)\n\n        if retrycount is not None:\n            if retrycount < 0:\n                raise DataError(\"XNACK retrycount must be >= 0\")\n            pieces.extend([b\"RETRYCOUNT\", retrycount])\n\n        if force:\n            pieces.append(b\"FORCE\")\n\n        return self.execute_command(\"XNACK\", *pieces)\n\n    @overload","sourceCodeStart":7662,"sourceCodeEnd":7698,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/commands/core.py#L7662-L7698","documentation":"Raised by xnack() when no message id is supplied. XNACK requires at least one id to negatively acknowledge. Check at core.py:7679: `if not ids`.","triggerScenarios":"r.xnack('s','g','FAIL') with no ids after mode, or r.xnack('s','g','SILENT').","commonSituations":"Calling XNACK on an empty ack-list from a batch processor; spreading an empty ids tuple; forgetting that mode is a positional arg followed by *ids.","solutions":["Pass at least one id after mode: r.xnack(name, group, mode, id1, ...).","Guard: if ids: r.xnack(name, group, mode, *ids)."],"exampleFix":"# before\nr.xnack('s','g','FAIL')\n# after\nif bad_ids:\n    r.xnack('s','g','FAIL', *bad_ids)","handlingStrategy":"validation","validationCode":"if not ids:\n    raise ValueError('xnack needs >= 1 id')\nr.xnack(name, group, mode, *ids, retrycount=retrycount, force=force)","typeGuard":null,"tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    r.xnack(name, group, mode, *ids)\nexcept DataError:\n    pass  # nothing to nack","preventionTips":["Short-circuit XNACK when the id list is empty."],"tags":["redis","streams","xnack","validation","empty-argument"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}