{"id":"7f66035bab638a90","repo":"redis/redis-py","slug":"xnack-mode-must-be-one-of-silent-fail-fatal","errorCode":null,"errorMessage":"XNACK mode must be one of: SILENT, FAIL, FATAL","messagePattern":"XNACK mode must be one of: SILENT, FAIL, FATAL","errorType":"validation","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/core.py","lineNumber":7683,"sourceCode":"                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\n    def xpending(\n        self: SyncClientProtocol, name: KeyT, groupname: GroupT\n    ) -> dict[str, Any]: ...","sourceCodeStart":7665,"sourceCodeEnd":7701,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/commands/core.py#L7665-L7701","documentation":"Raised by xnack() when `mode` is not one of SILENT, FAIL, FATAL. Case-SENSITIVE set membership at core.py:7682. mode is a required positional argument (no default).","triggerScenarios":"xnack('s','g','NACK'), =None, ='fail' (lowercase), ='silent'. Only exact 'SILENT','FAIL','FATAL' pass.","commonSituations":"Passing lowercase from config/UI; reusing an XCLAIM/xpending vocabulary; forgetting mode is required and shifting ids into its slot; passing None because the caller expected a default.","solutions":["Pass one of the exact uppercase tokens.","Map/normalize: mode = str(raw).upper(); assert mode in {'SILENT','FAIL','FATAL'}.","Remember the semantics: SILENT=consumer shutting down, FAIL=unable to process, FATAL=poison message."],"exampleFix":"# before\nr.xnack('s','g','fail', *ids)  # lowercase\n# after\nr.xnack('s','g','FAIL', *ids)","handlingStrategy":"validation","validationCode":"VALID = {'SILENT','FAIL','FATAL'}\nmode = str(raw).upper()\nif mode not in VALID:\n    raise ValueError(f'mode must be one of {VALID}')\nr.xnack(name, group, mode, *ids)","typeGuard":"def is_valid_xnack_mode(v) -> bool:\n    return v in {'SILENT','FAIL','FATAL'}","tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    r.xnack(name, group, mode, *ids)\nexcept DataError:\n    r.xnack(name, group, 'FAIL', *ids)","preventionTips":["mode is required and case-sensitive.","Pick FAIL for transient errors, FATAL for poison messages, SILENT for clean shutdown."],"tags":["redis","streams","xnack","validation","enum-error"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}