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 Redis.xnack() when mode is not one of 'SILENT', 'FAIL', 'FATAL'. These control delivery-counter behavior: SILENT decrements, FAIL leaves unchanged, FATAL sets to max. Case-sensitive uppercase.
Solutions
- Use one of the exact uppercase literals: 'SILENT', 'FAIL', 'FATAL'.
- Coerce config: mode = str(mode).upper(); validate membership.
Example fix
# before
r.xnack('mystream', 'grp', 'silent', '1-0')
# after
r.xnack('mystream', 'grp', 'SILENT', '1-0') Defensive patterns
Strategy: validation
Validate before calling
MODES = {'SILENT', 'FAIL', 'FATAL'}
mode = str(mode).upper()
if mode not in MODES:
raise ValueError('mode must be one of %s' % MODES) Type guard
lambda m: m in {'SILENT', 'FAIL', 'FATAL'} Try / catch
from redis.exceptions import DataError
try:
r.xnack('mystream', 'grp', mode, *ids)
except DataError as e:
log.warning('bad xnack mode %r: %s', mode, e) Prevention
- Uppercase and validate mode at the config boundary; case-sensitive.
When it happens
Trigger: Calling r.xnack('mystream', 'grp', 'silent') (lowercase), mode='RETRY', or a typo like 'SILENT '.
Common situations: Lowercasing from config, guessing the mode name, or confusing with XDELEX policies.
Related errors
- XDELEX ref_policy must be one of: KEEPREF, DELREF, ACKED
- XNACK requires at least one message ID
- XNACK retrycount must be >= 0
- CLIENT KILL type must be one of
- if XPENDING is provided with idle time or consumername, it…
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/7f66035bab638a90.
Report an issue: GitHub.
Appendix: 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 6a6b581b48)