redis/redis-py · error · DataError

At least one key must be provided.

Error message

At least one key must be provided.

What it means

Raised by the private TS.NRANGE / TS.NREVRANGE parameter builder when the keys list is empty. These multi-key commands require at least one time-series key; an empty list cannot produce a valid command.

Solutions

  1. Pass a non-empty list of time-series keys.
  2. Guard at the call site: skip the query or fetch keys first when the list is empty.
  3. If you intended a single-series range, use ts().range(...) with one key instead of nrange.

Example fix

// before
client.ts().nrange([], from_time, to_time)
// after
if keys:
    client.ts().nrange(keys, from_time, to_time)
# or single-series
client.ts().range(single_key, from_time, to_time)
Defensive patterns

Strategy: validation

Validate before calling

if not keys:
    raise ValueError('keys list is empty')
client.ts().nrange(keys, from_time, to_time)

Type guard

def is_nonempty_key_list(keys) -> bool:
    return isinstance(keys, (list, tuple)) and len(keys) > 0

Try / catch

from redis.exceptions import DataError
try:
    client.ts().nrange(keys, from_time, to_time)
except DataError:
    return []

Prevention

When it happens

Trigger: Calling client.ts().nrange([], from_time, to_time, ...) or nrevrange with an empty keys list.

Common situations: Dynamically resolving keys from a filter/lookup that returned nothing. Passing the wrong variable (e.g., a count instead of the key list).

Related errors


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

Appendix: source

Thrown at redis/commands/timeseries/commands.py:1106

    def __n_range_params(
        self,
        keys: List[KeyT],
        from_time: int | str,
        to_time: int | str,
        count: int | None,
        aggregators: str | list[str] | None,
        bucket_size_msec: int | None,
        filter_by_ts: List[int] | None,
        filter_by_min_value: int | None,
        filter_by_max_value: int | None,
        align: int | str | None,
        latest: bool | None,
        bucket_timestamp: str | None,
        empty: bool | None,
    ):
        """Create TS.NRANGE and TS.NREVRANGE arguments."""
        if not keys:
            raise DataError("At least one key must be provided.")
        # numkeys is always derived from the key list and precedes the keys;
        # key order and duplicates are preserved and map to output columns.
        params: list[EncodableT] = [len(keys), *keys, from_time, to_time]
        self._append_latest(params, latest)
        self._append_filer_by_ts(params, filter_by_ts)
        self._append_filer_by_value(params, filter_by_min_value, filter_by_max_value)
        self._append_count(params, count)
        self._append_align(params, align)
        self._append_n_aggregation(params, aggregators, bucket_size_msec, len(keys))
        self._append_bucket_timestamp(params, bucket_timestamp)
        self._append_empty(params, empty)

        return params

    @overload
    def nrange(
        self: SyncClientProtocol,
        keys: List[KeyT],

View on GitHub (pinned to 6a6b581b48)