{"record":{"id":"4721d4afd128b155","repo":"redis/redis-py","slug":"at-least-one-key-must-be-provided","errorCode":null,"errorMessage":"At least one key must be provided.","messagePattern":"At least one key must be provided\\.","errorType":"validation","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/commands/timeseries/commands.py","lineNumber":1106,"sourceCode":"    def __n_range_params(\n        self,\n        keys: List[KeyT],\n        from_time: int | str,\n        to_time: int | str,\n        count: int | None,\n        aggregators: str | list[str] | None,\n        bucket_size_msec: int | None,\n        filter_by_ts: List[int] | None,\n        filter_by_min_value: int | None,\n        filter_by_max_value: int | None,\n        align: int | str | None,\n        latest: bool | None,\n        bucket_timestamp: str | None,\n        empty: bool | None,\n    ):\n        \"\"\"Create TS.NRANGE and TS.NREVRANGE arguments.\"\"\"\n        if not keys:\n            raise DataError(\"At least one key must be provided.\")\n        # numkeys is always derived from the key list and precedes the keys;\n        # key order and duplicates are preserved and map to output columns.\n        params: list[EncodableT] = [len(keys), *keys, from_time, to_time]\n        self._append_latest(params, latest)\n        self._append_filer_by_ts(params, filter_by_ts)\n        self._append_filer_by_value(params, filter_by_min_value, filter_by_max_value)\n        self._append_count(params, count)\n        self._append_align(params, align)\n        self._append_n_aggregation(params, aggregators, bucket_size_msec, len(keys))\n        self._append_bucket_timestamp(params, bucket_timestamp)\n        self._append_empty(params, empty)\n\n        return params\n\n    @overload\n    def nrange(\n        self: SyncClientProtocol,\n        keys: List[KeyT],","sourceCodeStart":1088,"sourceCodeEnd":1124,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/commands/timeseries/commands.py#L1088-L1124","documentation":"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.","triggerScenarios":"Calling client.ts().nrange([], from_time, to_time, ...) or nrevrange with an empty keys list.","commonSituations":"Dynamically resolving keys from a filter/lookup that returned nothing. Passing the wrong variable (e.g., a count instead of the key list).","solutions":["Pass a non-empty list of time-series keys.","Guard at the call site: skip the query or fetch keys first when the list is empty.","If you intended a single-series range, use ts().range(...) with one key instead of nrange."],"exampleFix":"// before\nclient.ts().nrange([], from_time, to_time)\n// after\nif keys:\n    client.ts().nrange(keys, from_time, to_time)\n# or single-series\nclient.ts().range(single_key, from_time, to_time)","handlingStrategy":"validation","validationCode":"if not keys:\n    raise ValueError('keys list is empty')\nclient.ts().nrange(keys, from_time, to_time)","typeGuard":"def is_nonempty_key_list(keys) -> bool:\n    return isinstance(keys, (list, tuple)) and len(keys) > 0","tryCatchPattern":"from redis.exceptions import DataError\ntry:\n    client.ts().nrange(keys, from_time, to_time)\nexcept DataError:\n    return []","preventionTips":["Resolve keys before calling nrange and short-circuit on empty.","Use range() for single-key queries."],"tags":["redistimeseries","nrange","argument-validation"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}