redis/redis-py · error · DataError

XRANGE count must be a positive integer

Error message

XRANGE count must be a positive integer

What it means

Raised by xrange() when the optional count argument is either not an int (isinstance check) or is less than 1. Unlike XPENDING, this uses a strict isinstance(count, int) check, so floats and numeric strings ('10', 10.0) are rejected even if their value is positive. The library enforces this because XRANGE COUNT on the server requires a positive integer.

Source

Thrown at redis/commands/core.py:7845

        Read stream values within an interval.

        name: name of the stream.

        start: first stream ID. defaults to '-',
               meaning the earliest available.

        finish: last stream ID. defaults to '+',
                meaning the latest available.

        count: if set, only return this many items, beginning with the
               earliest available.

        For more information, see https://redis.io/commands/xrange
        """
        pieces = [min, max]
        if count is not None:
            if not isinstance(count, int) or count < 1:
                raise DataError("XRANGE count must be a positive integer")
            pieces.append(b"COUNT")
            pieces.append(str(count))

        return self.execute_command("XRANGE", name, *pieces, keys=[name])

    @overload
    def xread(
        self: SyncClientProtocol,
        streams: Dict[KeyT, StreamIdT],
        count: int | None = None,
        block: int | None = None,
        max_count: int | None = None,
        max_size: int | None = None,
    ) -> XReadResponse: ...

    @overload
    def xread(
        self: AsyncClientProtocol,

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Pass an int >= 1, or omit count / pass None for no limit.
  2. Coerce incoming value explicitly: count = int(count) if count else None.
  3. Validate with isinstance(count, int) and count >= 1 before the call.

Example fix

// before
client.xrange("s", "-", "+", count="50")
// after
client.xrange("s", "-", "+", count=50)
Defensive patterns

Strategy: validation

Validate before calling

def normalize_count(v):
    if v is None:
        return None
    if not isinstance(v, int) or isinstance(v, bool):
        raise TypeError(f"count must be int, got {type(v)}")
    if v < 1:
        raise ValueError(f"count must be >= 1, got {v}")
    return v

count = normalize_count(raw_count)
client.xrange("s", "-", "+", count=count)

Type guard

def is_positive_int(v) -> bool:
    return isinstance(v, int) and not isinstance(v, bool) and v >= 1

Try / catch

from redis.exceptions import DataError
try:
    client.xrange("s", "-", "+", count=count)
except DataError as e:
    if "XRANGE count" in str(e):
        count = int(count)
        client.xrange("s", "-", "+", count=count) if count >= 1 else None
    else:
        raise

Prevention

When it happens

Trigger: Call client.xrange(name, min, max, count) with count as a float (10.0), a string ('10'), zero, a negative number, or any non-int type. Booleans pass isinstance(True, int) but produce a COUNT of 1.

Common situations: Loading count from JSON/CSV where it arrives as a string; dividing values producing floats (e.g. n/2 when n is even); passing 0 expecting 'no limit' (use None instead).

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/24924b8a8d482a8c.json. Report an issue: GitHub.