redis/jedis · error · IllegalArgumentException
Aggregation requires exactly one spec per key: numkeys=
Error message
Aggregation requires exactly one spec per key: numkeys=${numKeys} but got ${aggregators.length} aggregation spec(s) What it means
TSNRangeParams.validateAggregationForKeys(int numKeys) is called by UnifiedJedis for TS.MRANGE/TS.MREVRANGE when the number of keys is known. If aggregation specs were set but their count differs from numKeys, the server contract (one AGGREGATION spec per key) would be violated, so the client throws IllegalArgumentException. It is a no-op when aggregation was never set (aggregators == null).
Solutions
- Align the spec array length with the number of keys: supply exactly one AggregationType[] per key before executing the command.
- If the same params must serve varying key counts, build a fresh TSNRangeParams per call and size the specs to keys.size().
- Call params.validateAggregationForKeys(keys.size()) yourself before executing to catch mismatches at the call site with a clear stack trace.
Example fix
// before
params.aggregation(new AggregationType[][]{{AggregationType.AVG}}, 60000); // 1 spec
jedis.tsNRange(keys, params); // keys.size() == 3 -> throws
// after
AggregationType[][] specs = new AggregationType[keys.size()][];
for (int i = 0; i < keys.size(); i++) specs[i] = new AggregationType[]{AggregationType.AVG};
params.aggregation(specs, 60000);
jedis.tsNRange(keys, params); Defensive patterns
Strategy: validation
Validate before calling
if (params != null && keys.size() != expectedSpecCount) {
throw new IllegalStateException("specs=" + expectedSpecCount + " keys=" + keys.size());
}
jedis.tsNRange(keys, params); Try / catch
try { jedis.tsNRange(keys, params); } catch (IllegalArgumentException e) { log.error("aggregation spec count mismatch for {} keys", keys.size(), e); throw e; } Prevention
- Build specs from the same keys collection so lengths stay equal
- Call validateAggregationForKeys(keys.size()) before executing
- Avoid sharing one TSNRangeParams across commands with different key counts
When it happens
Trigger: Executing tsNRange/tsNRevRange with N keys while TSNRangeParams.aggregation(...) was given fewer or more than N AggregationType[] specs, e.g. 3 keys with only 2 specs.
Common situations: Key list built dynamically at runtime while the spec array was built from a different (stale or filtered) collection; copy-pasted params objects reused across commands with different key counts.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Aggregators must be non-null and non-empty
- Aggregators must not contain null elements
- FILTER arguments must be set.
- EXCLUDEEMPTY is not allowed with GROUPBY.
- Aggregators must be non-null and non-empty
AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08).
Data as JSON: /api/errors/dd366b551216bc86.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/timeseries/TSNRangeParams.java:197
}
this.aggregators = aggregators;
this.bucketDuration = bucketDuration;
return this;
}
/**
* Validates this aggregation configuration against the actual number of command keys.
* {@code TS.NRANGE}/{@code TS.NREVRANGE} require exactly one aggregation spec per key, so when
* aggregation is set the number of specs must equal {@code numKeys}. This mirrors the server rule
* client-side so callers fail fast instead of relying on the {@code the number of AGGREGATION
* arguments must be equal to numkeys} error. No-op when aggregation is not set.
* @param numKeys number of keys passed to the command
* @throws IllegalArgumentException if aggregation is set and the spec count differs from
* {@code numKeys}
*/
public void validateAggregationForKeys(int numKeys) {
if (aggregators != null && aggregators.length != numKeys) {
throw new IllegalArgumentException("Aggregation requires exactly one spec per key: numkeys="
+ numKeys + " but got " + aggregators.length + " aggregation spec(s)");
}
}
/**
* This requires AGGREGATION.
*/
public TSNRangeParams bucketTimestamp(String bucketTimestamp) {
this.bucketTimestamp = encode(bucketTimestamp);
return this;
}
/**
* This requires AGGREGATION.
*/
public TSNRangeParams bucketTimestampLow() {
this.bucketTimestamp = MINUS;
return this;View on GitHub (pinned to 6dac31d4c2)