go-redis/redis · error
redis: invalid timeseries aggregator at index %d[%d]: %d
Error message
redis: invalid timeseries aggregator at index %d[%d]: %d
What it means
Returned by buildNRangeAggregationArgs when an inner aggregator's String() returns empty, i.e. the value is outside the defined iota range (greater than CountAll). Reports the outer index, inner index, and raw int value to locate the bad cast.
Source
Thrown at timeseries_commands.go:1391
// lists one or more aggregators for its key and is emitted as a single comma-joined wire
// token; specs for different keys are separate wire tokens.
func buildNRangeAggregationArgs(keys []string, aggregators [][]Aggregator) ([]string, error) {
if len(aggregators) != len(keys) {
return nil, fmt.Errorf("redis: TS.NRANGE/TS.NREVRANGE requires exactly %d aggregator spec(s), got %d", len(keys), len(aggregators))
}
parts := make([]string, len(aggregators))
for i, spec := range aggregators {
if len(spec) == 0 {
return nil, fmt.Errorf("redis: empty timeseries aggregator spec at index %d", i)
}
names := make([]string, len(spec))
for j, agg := range spec {
if agg == Invalid {
return nil, fmt.Errorf("redis: invalid timeseries aggregator at index %d[%d]: Invalid (%d)", i, j, agg)
}
s := agg.String()
if s == "" {
return nil, fmt.Errorf("redis: invalid timeseries aggregator at index %d[%d]: %d", i, j, agg)
}
names[j] = s
}
parts[i] = strings.Join(names, ",")
}
return parts, nil
}
// appendNRangeOptions appends optional TS.NRANGE / TS.NREVRANGE arguments to args.
func appendNRangeOptions(
args []interface{},
keys []string,
latest bool,
filterByTS []int,
filterByValue []float64,
count int,
align interface{},
aggregators [][]Aggregator,View on GitHub (pinned to 36d97525cd)
Solutions
- Validate each aggregator is within [redis.Avg, redis.CountAll] before building specs.
- Use named constants instead of casting raw ints.
- Reject unknown aggregator ids when parsing external input.
Example fix
// before
opts := &redis.TSNRangeOptions{
Aggregators: [][]redis.Aggregator{{redis.Aggregator(20)}},
}
// after
opts := &redis.TSNRangeOptions{
Aggregators: [][]redis.Aggregator{{redis.Avg}},
} Defensive patterns
Strategy: validation
Validate before calling
func validateNRangeSpecs(aggs [][]redis.Aggregator) error {
for i, spec := range aggs {
for j, a := range spec {
if a < redis.Avg || a > redis.CountAll {
return fmt.Errorf("aggregator [%d][%d] out of range: %d", i, j, a)
}
}
}
return nil
} Type guard
func isValidAggregator(a redis.Aggregator) bool { return a >= redis.Avg && a <= redis.CountAll } Prevention
- Bounds-check cast aggregators against the valid iota range before use.
- Never cast untrusted ints to redis.Aggregator without validation.
- Use named constants instead of raw casts.
When it happens
Trigger: Passing Aggregators containing an Aggregator produced by an unchecked cast such as redis.Aggregator(20), or deserialised from an out-of-range index.
Common situations: Mapping an external/config aggregator id into the Aggregator type without bounds-checking, or a lookup table with a wrong ordinal.
Related errors
- redis: invalid timeseries aggregator at index %d: %d
- redis: invalid timeseries aggregator: %d
- redis: TS.NRANGE/TS.NREVRANGE requires exactly %d aggregator
- redis: empty timeseries aggregator spec at index %d
- redis: invalid timeseries aggregator at index %d[%d]: Invali
AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06).
Data as JSON: /data/errors/cc3e15685467facb.json.
Report an issue: GitHub.