redis/redis-py · error · DataError

Both start and end must be specified

Error message

Both start and end must be specified

What it means

bitcount() requires start and end to be supplied together or both omitted; specifying only one is ambiguous (Redis BITCOUNT needs a closed range). The guard at core.py:2667 rejects the half-specified range with DataError before sending a malformed command.

Source

Thrown at redis/commands/core.py:2668

    def bitcount(
        self,
        key: KeyT,
        start: int | None = None,
        end: int | None = None,
        mode: str | None = None,
    ) -> int | Awaitable[int]:
        """
        Returns the count of set bits in the value of ``key``.  Optional
        ``start`` and ``end`` parameters indicate which bytes to consider

        For more information, see https://redis.io/commands/bitcount
        """
        params = [key]
        if start is not None and end is not None:
            params.append(start)
            params.append(end)
        elif (start is not None and end is None) or (end is not None and start is None):
            raise DataError("Both start and end must be specified")
        if mode is not None:
            params.append(mode)
        return self.execute_command("BITCOUNT", *params, keys=[key])

    def bitfield(
        self: Union["redis.client.Redis", "redis.asyncio.client.Redis"],
        key: KeyT,
        default_overflow: str | None = None,
    ) -> BitFieldOperation:
        """
        Return a BitFieldOperation instance to conveniently construct one or
        more bitfield operations on ``key``.

        For more information, see https://redis.io/commands/bitfield
        """
        return BitFieldOperation(self, key, default_overflow=default_overflow)

    @overload

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Pass both start and end as a pair, e.g. r.bitcount('key', start=0, end=10).
  2. Omit both to count over the whole string, e.g. r.bitcount('key').
  3. When building params dynamically, default the missing bound explicitly (start=0 when only end is given, end=-1 when only start is given).

Example fix

# before
r.bitcount('key', start=0)

# after
r.bitcount('key', start=0, end=10)
Defensive patterns

Strategy: validation

Validate before calling

if (start is None) != (end is None):
    raise ValueError('bitcount: provide both start and end, or neither')
r.bitcount('key', start=start, end=end)

Type guard

def valid_bitcount_range(start, end) -> bool:
    return (start is None) == (end is None)

Try / catch

from redis.exceptions import DataError
try:
    r.bitcount('key', start=start, end=end)
except DataError as e:
    if 'Both start and end' in str(e):
        end = -1 if end is None else end
        start = 0 if start is None else start
        r.bitcount('key', start=start, end=end)
    else:
        raise

Prevention

When it happens

Trigger: r.bitcount('key', start=0) with no end, or r.bitcount('key', end=10) with no start. Both trigger the XOR branch in the elif.

Common situations: Dynamically building kwargs from optional CLI flags or config where only one bound was populated; refactoring from an older API that defaulted the missing bound.

Related errors


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