redis/redis-py · error · DataError
XNACK mode must be one of: SILENT, FAIL, FATAL
Error message
XNACK mode must be one of: SILENT, FAIL, FATAL
What it means
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).
Source
Thrown at redis/commands/core.py:7683
SILENT: consumer shutting down; decrements delivery counter.
FAIL: consumer unable to process; delivery counter unchanged.
FATAL: invalid/malicious message; delivery counter set to max.
*ids: one or more message IDs to NACK.
retrycount: optional integer >= 0. Overrides the mode's implicit
delivery counter adjustment with an exact value.
force: if True, creates a new unowned PEL entry for any ID not
already in the group's PEL.
Returns:
The number of messages successfully NACKed.
For more information, see https://redis.io/commands/xnack
"""
if not ids:
raise DataError("XNACK requires at least one message ID")
if mode not in {"SILENT", "FAIL", "FATAL"}:
raise DataError("XNACK mode must be one of: SILENT, FAIL, FATAL")
pieces: list = [name, groupname, mode, "IDS", len(ids)]
pieces.extend(ids)
if retrycount is not None:
if retrycount < 0:
raise DataError("XNACK retrycount must be >= 0")
pieces.extend([b"RETRYCOUNT", retrycount])
if force:
pieces.append(b"FORCE")
return self.execute_command("XNACK", *pieces)
@overload
def xpending(
self: SyncClientProtocol, name: KeyT, groupname: GroupT
) -> dict[str, Any]: ...View on GitHub (pinned to da03cdc7e8)
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.
Example fix
# before
r.xnack('s','g','fail', *ids) # lowercase
# after
r.xnack('s','g','FAIL', *ids) Defensive patterns
Strategy: validation
Validate before calling
VALID = {'SILENT','FAIL','FATAL'}
mode = str(raw).upper()
if mode not in VALID:
raise ValueError(f'mode must be one of {VALID}')
r.xnack(name, group, mode, *ids) Type guard
def is_valid_xnack_mode(v) -> bool:
return v in {'SILENT','FAIL','FATAL'} Try / catch
from redis.exceptions import DataError
try:
r.xnack(name, group, mode, *ids)
except DataError:
r.xnack(name, group, 'FAIL', *ids) Prevention
- mode is required and case-sensitive.
- Pick FAIL for transient errors, FATAL for poison messages, SILENT for clean shutdown.
When it happens
Trigger: xnack('s','g','NACK'), =None, ='fail' (lowercase), ='silent'. Only exact 'SILENT','FAIL','FATAL' pass.
Common situations: 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.
Related errors
- XDELEX ref_policy must be one of: KEEPREF, DELREF, ACKED
- XNACK requires at least one message ID
- XNACK retrycount must be >= 0
- XCFGSET requires at least one of idmp_duration or idmp_maxsi
- XCFGSET idmp_duration must be an integer between 1 and 300
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/7f66035bab638a90.json.
Report an issue: GitHub.