redis/redis-py · error · DataError
XNACK retrycount must be >= 0
Error message
XNACK retrycount must be >= 0
What it means
Raised by Redis.xnack() when retrycount is provided and is < 0. Note: there is NO isinstance check here -- only `if retrycount < 0:`. So a non-int that supports comparison may slip through to the server; only a negative comparable value raises. retrycount overrides the mode's implicit delivery-counter adjustment.
Solutions
- Pass a non-negative int for retrycount, or omit it.
- Clamp: retrycount = max(0, int(retrycount)).
Example fix
# before
r.xnack('mystream', 'grp', 'FAIL', '1-0', retrycount=delivered - maxr) # negative
# after
r.xnack('mystream', 'grp', 'FAIL', '1-0', retrycount=max(0, delivered - maxr)) Defensive patterns
Strategy: validation
Validate before calling
if retrycount is not None:
retrycount = int(retrycount)
if retrycount < 0:
raise ValueError('retrycount must be >= 0') Type guard
lambda v: v is None or (isinstance(v, int) and not isinstance(v, bool) and v >= 0)
Try / catch
from redis.exceptions import DataError
try:
r.xnack('mystream', 'grp', 'FAIL', *ids, retrycount=rc)
except DataError as e:
log.warning('bad xnack retrycount %r: %s', rc, e) Prevention
- Clamp retrycount to >= 0; note the guard is value-only, so coerce type yourself.
When it happens
Trigger: Calling r.xnack('mystream', 'grp', 'FAIL', '1-0', retrycount=-1). A float like -0.5 also raises (< 0). A non-comparable retrycount would raise TypeError, not DataError.
Common situations: Computing retrycount as delivered - max_retries and underflowing below zero.
Related errors
- XNACK mode must be one of: SILENT, FAIL, FATAL
- XNACK requires at least one message ID
- if XPENDING is provided with idle time or consumername, it…
- XAUTOCLAIM min_idle_time must be a nonnegative integer
- XCFGSET idmp_duration must be an integer between 1 and 300
AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10).
Data as JSON: /api/errors/1a4363c8bafb59d4.
Report an issue: GitHub.
Appendix: source
Thrown at redis/commands/core.py:7690
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]: ...
@overload
def xpending(
self: AsyncClientProtocol, name: KeyT, groupname: GroupT
) -> Awaitable[dict[str, Any]]: ...
def xpending(View on GitHub (pinned to 6a6b581b48)