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 RedisClient xrange() (redis/commands/core.py:7845) as a DataError when the count argument is present but is not a Python int or is less than 1. The library validates client-side before sending the XRANGE command so an invalid COUNT never reaches the server. Note bool is a subclass of int so True/False slip through; floats, strings, and zero/negative ints are rejected.

Solutions

  1. Pass an int >= 1 for count, or pass None / omit it when you want no limit.
  2. Coerce untrusted input: count = int(count) and guard count is not None and count >= 1 before calling.
  3. If you meant 'all entries', do not set count at all - None means unlimited.

Example fix

# before
client.xrange('mystream', '-', '+', count=os.environ['XRANGE_COUNT'])
# after
c = int(os.environ['XRANGE_COUNT'])
client.xrange('mystream', '-', '+', count=c if c >= 1 else None)
Defensive patterns

Strategy: validation

Validate before calling

def safe_xrange_count(count):
    if count is None:
        return None
    if not isinstance(count, int) or isinstance(count, bool) or count < 1:
        raise DataError('XRANGE count must be a positive int')
    return count

Type guard

from typing import Union

def is_valid_xrange_count(c) -> bool:
    return isinstance(c, int) and not isinstance(c, bool) and c >= 1

Try / catch

from redis.exceptions import DataError
try:
    client.xrange('s', '-', '+', count=user_count)
except DataError as e:
    if 'XRANGE count' in str(e):
        logger.warning('invalid count %r, retrying unlimited', user_count)
        client.xrange('s', '-', '+')
    else:
        raise

Prevention

When it happens

Trigger: Calling client.xrange(name, '-', '+', count=0), count=-5, count=1.5, count='10', or count=None-equivalent truthy non-int. Any non-int or count<1 value passed as the count kwarg triggers it.

Common situations: Reading count from untyped config/env (os.environ returns str), passing a float from a computed ratio without int(), defaulting count to 0 meaning 'no limit' (0 is invalid; use None), or off-by-one when paginating streams.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/24924b8a8d482a8c. Report an issue: GitHub.

Appendix: 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 6a6b581b48)