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 TS.NRANGE/TS.NREVRANGE parameter builder (__n_range_params) when the keys list is empty. These multi-key commands require at least one time-series key; an empty list would send numkeys=0, which the server rejects. The library validates client-side for a clearer error.

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 da03cdc7e8)

Solutions

  1. Ensure the keys list is non-empty before calling: if keys: client.ts().nrange(keys, '-', '+').
  2. Fix the upstream lookup so it returns the intended keys.
  3. If you genuinely have no keys, skip the query and return an empty result.

Example fix

// before
client.ts().nrange(keys, '-', '+')  # keys == []
// after
if keys:
    client.ts().nrange(keys, '-', '+')
Defensive patterns

Strategy: validation

Validate before calling

def safe_nrange(client, keys, frm, to, **kw):
    if not keys:
        return []
    return client.ts().nrange(list(keys), frm, to, **kw)

Type guard

def has_keys(keys) -> bool:
    return bool(keys)

Try / catch

try:
    client.ts().nrange(keys, '-', '+')
except Exception as e:
    if 'At least one key' in str(e):
        result = []
    else:
        raise

Prevention

When it happens

Trigger: Calling client.ts().nrange([], '-', '+') or client.ts().nrange(keys, '-', '+') where keys resolved to an empty list; same for nrevrange.

Common situations: Querying a list of keys produced by a label filter/lookup that matched nothing; fan-out from upstream that returned an empty set; defaulting keys to [] and forgetting to populate.

Related errors


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