redis/redis-py · error · DataError
AGGREGATION requires exactly one aggregation spec per key (e
Error message
AGGREGATION requires exactly one aggregation spec per key (expected {numkeys}, got {len(specs)}); a spec may list multiple comma-separated aggregators. What it means
Raised by _append_n_aggregation for TS.NRANGE/TS.NREVRANGE when the number of aggregation specs does not equal the number of queried keys. Each key needs exactly one spec token (which may itself list multiple comma-separated aggregators, e.g. 'avg,max'). A bare string is the spec for a single-key query.
Source
Thrown at redis/commands/timeseries/commands.py:2062
bucket_size_msec: int | None,
numkeys: int,
):
"""Append the AGGREGATION clause for TS.NRANGE / TS.NREVRANGE.
These commands take exactly one aggregation spec token per queried key;
a single aggregator is never broadcast across keys. A spec token may hold
several comma-separated aggregators, so the wire form is e.g.
``AGGREGATION avg,max sum 1000`` for two keys -- key 0 aggregated by both
``avg`` and ``max``, key 1 by ``sum``. Pass one spec string per key, each
optionally comma-joined (``["avg,max", "sum"]``); a bare string is the
spec for a single-key query. (Matches RedisTimeSeries PR #2079, which
replaced the earlier single comma-joined / broadcast token.)
"""
if aggregators is None:
return
specs = [aggregators] if isinstance(aggregators, str) else list(aggregators)
if len(specs) != numkeys:
raise DataError(
"AGGREGATION requires exactly one aggregation spec per key "
f"(expected {numkeys}, got {len(specs)}); a spec may list "
"multiple comma-separated aggregators."
)
params.extend(["AGGREGATION", *specs, bucket_size_msec])
@staticmethod
def _append_chunk_size(params: list[EncodableT], chunk_size: int | None):
"""Append CHUNK_SIZE property to params."""
if chunk_size is not None:
params.extend(["CHUNK_SIZE", chunk_size])
@staticmethod
def _append_duplicate_policy(
params: list[EncodableT], duplicate_policy: str | None
):
"""Append DUPLICATE_POLICY property to params."""
if duplicate_policy is not None:View on GitHub (pinned to da03cdc7e8)
Solutions
- Provide exactly one spec per key: for keys=[a,b] use aggregators=['avg','sum'] or aggregators=['avg,max','sum'].
- For a single key, pass a bare string: aggregators='avg'.
- When unsure, build the list dynamically: aggregators=['avg'] * len(keys).
Example fix
// before client.ts().nrange(['a','b'], '-', '+', aggregators=['avg']) // after client.ts().nrange(['a','b'], '-', '+', aggregators=['avg','sum'])
Defensive patterns
Strategy: validation
Validate before calling
def normalize_n_agg(keys, aggregators):
if aggregators is None:
return None
if isinstance(aggregators, str):
return aggregators if len(keys) == 1 else [aggregators] * len(keys)
specs = list(aggregators)
if len(specs) != len(keys):
raise ValueError(f"Expected {len(keys)} specs, got {len(specs)}")
return specs Type guard
def n_agg_matches(keys, aggregators) -> bool:
if aggregators is None:
return True
if isinstance(aggregators, str):
return len(keys) == 1
return len(list(aggregators)) == len(keys) Try / catch
try:
client.ts().nrange(keys, '-', '+', aggregators=agg)
except Exception as e:
if 'exactly one aggregation spec' in str(e):
client.ts().nrange(keys, '-', '+', aggregators=[agg] * len(keys) if isinstance(agg, str) else agg)
else:
raise Prevention
- Provide one spec per key (a string is shorthand for a single key).
- Use comma-joined specs for multi-aggregator per key: ['avg,max','sum'].
- Build the spec list from len(keys) when broadcasting one aggregator.
When it happens
Trigger: client.ts().nrange(['a','b'], '-', '+', aggregators=['avg']) (1 spec, 2 keys); or nrange(['a'], '-', '+', aggregators=['avg','sum']) (2 specs, 1 key). Correct multi-key form: aggregators=['avg,max','sum'] for keys=[a,b].
Common situations: Assuming one aggregator broadcasts across all keys; passing a flat list of aggregators instead of one spec per key; migrating from the old comma-joined broadcast syntax.
Related errors
- At least one key must be provided.
- GROUPBY is not allowed when multiple aggregators are specifi
- EXCLUDEEMPTY is not allowed with GROUPBY
- filters cannot be an empty collection; pass None to query al
- with_labels and select_labels cannot be provided together.
AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04).
Data as JSON: /data/errors/cc7b0aa25a99ec39.json.
Report an issue: GitHub.