redis/redis-py · error · DataError
XNACK retrycount must be >= 0
Error message
XNACK retrycount must be >= 0
What it means
Raised by xnack() when optional `retrycount` is negative. CAUTION: unlike xclaim, there is NO isinstance check here - only `retrycount < 0` (core.py:7689). So a negative int raises DataError correctly, but a non-numeric retrycount (e.g. a string) makes `str < 0` raise an uncaught TypeError that crashes the call.
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 da03cdc7e8)
Solutions
- Pass a non-negative int, or None to let the mode decide.
- Coerce and clamp: rc = None if raw is None else max(0, int(raw)).
- Do NOT pass a string - it will crash with TypeError before this DataError can fire.
Example fix
# before
r.xnack('s','g','FAIL', *ids, retrycount=attempts) # attempts == -1
# after
r.xnack('s','g','FAIL', *ids, retrycount=None if attempts is None else max(0, int(attempts))) Defensive patterns
Strategy: validation
Validate before calling
rc = None if retrycount is None else max(0, int(retrycount)) r.xnack(name, group, mode, *ids, retrycount=rc)
Type guard
def opt_nonneg_int(v):
return None if v is None else max(0, int(v)) Try / catch
from redis.exceptions import DataError
try:
r.xnack(name, group, mode, *ids, retrycount=rc)
except DataError:
r.xnack(name, group, mode, *ids, retrycount=max(0, int(rc))) Prevention
- Coerce retrycount to int yourself - the guard does not type-check.
- Clamp negatives to 0 or pass None.
When it happens
Trigger: xnack(..., retrycount=-1) (int) -> DataError. xnack(..., retrycount='-1') (str) -> TypeError, NOT this DataError. retrycount=None or >=0 is fine.
Common situations: Passing retrycount from JSON as a string; a decremented counter that went below zero; expecting the guard to also validate type.
Related errors
- XAUTOCLAIM min_idle_time must be a nonnegative integer
- XPENDING idle must be a integer >= 0
- XCFGSET idmp_duration must be an integer between 1 and 300
- XCFGSET idmp_maxsize must be an integer between 1 and 1,000,
- XPENDING count must be a integer >= 0
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/1a4363c8bafb59d4.json.
Report an issue: GitHub.