{"id":"5bf7bf5e73d72791","repo":"go-redis/redis","slug":"redis-invalid-timeseries-aggregator-at-index-d-5bf7bf","errorCode":null,"errorMessage":"redis: invalid timeseries aggregator at index %d: %d","messagePattern":"redis: invalid timeseries aggregator at index (.+?): (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"timeseries_commands.go","lineNumber":197,"sourceCode":"\tif len(aggregators) == 0 {\n\t\tif aggregator == Invalid {\n\t\t\treturn \"\", 0, nil\n\t\t}\n\t\taggregationArg, err := formatAggregatorArg(aggregator)\n\t\tif err != nil {\n\t\t\treturn \"\", 0, err\n\t\t}\n\t\treturn aggregationArg, 1, nil\n\t}\n\n\tparts := make([]string, len(aggregators))\n\tfor i, agg := range aggregators {\n\t\tif agg == Invalid {\n\t\t\treturn \"\", 0, fmt.Errorf(\"redis: invalid timeseries aggregator at index %d: Invalid (%d)\", i, agg)\n\t\t}\n\t\taggregationArg, err := formatAggregatorArg(agg)\n\t\tif err != nil {\n\t\t\treturn \"\", 0, fmt.Errorf(\"redis: invalid timeseries aggregator at index %d: %d\", i, agg)\n\t\t}\n\t\tparts[i] = aggregationArg\n\t}\n\n\treturn strings.Join(parts, \",\"), len(parts), nil\n}\n\nfunc formatAggregatorArg(aggregator Aggregator) (string, error) {\n\taggregationArg := aggregator.String()\n\tif aggregationArg == \"\" {\n\t\treturn \"\", fmt.Errorf(\"redis: invalid timeseries aggregator: %d\", aggregator)\n\t}\n\treturn aggregationArg, nil\n}\n\ntype TSRangeOptions struct {\n\tLatest        bool\n\tFilterByTS    []int","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/timeseries_commands.go#L179-L215","documentation":"Returned by formatAggregationArgs when formatAggregatorArg fails for an element of the Aggregators slice. formatAggregatorArg fails when aggregator.String() returns empty, which happens for any Aggregator value outside the defined iota range (i.e. a value cast from an int greater than CountAll). The message reports the offending index and raw int value.","triggerScenarios":"Passing an Aggregators slice containing a value produced by an illegal cast, e.g. redis.Aggregator(99), or arithmetic that overflows past CountAll (13).","commonSituations":"Deserialising an aggregator index from an untrusted/config source without bounds-checking, or a typo in a lookup table mapping strings to ints.","solutions":["Validate that each aggregator is within the known range [Avg(1), CountAll(13)] before building the slice.","Reject or clamp out-of-range values when parsing external aggregator indices.","Use the named constants (redis.Avg … redis.CountAll) instead of casting raw ints."],"exampleFix":"// before\nagg := redis.Aggregator(getAggregatorIdFromConfig()) // returns 99\nopts := &redis.TSRangeOptions{Aggregators: []redis.Aggregator{agg}}\n\n// after\nif agg > redis.CountAll || agg <= redis.Invalid {\n    return fmt.Errorf(\"unsupported aggregator id %d\", agg)\n}\nopts := &redis.TSRangeOptions{Aggregators: []redis.Aggregator{agg}}","handlingStrategy":"validation","validationCode":"func validAggregators(aggs []redis.Aggregator) error {\n    for i, a := range aggs {\n        if a < redis.Avg || a > redis.CountAll {\n            return fmt.Errorf(\"aggregator at index %d out of range: %d\", i, a)\n        }\n    }\n    return nil\n}","typeGuard":"func isValidAggregator(a redis.Aggregator) bool { return a >= redis.Avg && a <= redis.CountAll }","tryCatchPattern":null,"preventionTips":["Bounds-check any cast aggregator against [redis.Avg, redis.CountAll].","Use named constants instead of redis.Aggregator(intVar).","Reject unknown aggregator ids at the config-parsing boundary."],"tags":["timeseries","aggregator","validation","enum-out-of-range"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}