{"id":"cfd9c8119f6998e4","repo":"go-redis/redis","slug":"redis-ts-nrange-ts-nrevrange-requires-exactly-d","errorCode":null,"errorMessage":"redis: TS.NRANGE/TS.NREVRANGE requires exactly %d aggregator spec(s), got %d","messagePattern":"redis: TS\\.NRANGE/TS\\.NREVRANGE requires exactly (.+?) aggregator spec\\(s\\), got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"timeseries_commands.go","lineNumber":1377,"sourceCode":"\t\t\tif cmd.val[i].Values != nil {\n\t\t\t\tval[i].Values = make([]float64, len(cmd.val[i].Values))\n\t\t\t\tcopy(val[i].Values, cmd.val[i].Values)\n\t\t\t}\n\t\t}\n\t}\n\treturn &TSNRangePivotRowSliceCmd{\n\t\tbaseCmd: cmd.cloneBaseCmd(),\n\t\tval:     val,\n\t}\n}\n\n// buildNRangeAggregationArgs validates and returns one aggregator spec string per key for\n// TS.NRANGE / TS.NREVRANGE. The number of specs must equal the number of keys. Each spec\n// lists one or more aggregators for its key and is emitted as a single comma-joined wire\n// token; specs for different keys are separate wire tokens.\nfunc buildNRangeAggregationArgs(keys []string, aggregators [][]Aggregator) ([]string, error) {\n\tif len(aggregators) != len(keys) {\n\t\treturn nil, fmt.Errorf(\"redis: TS.NRANGE/TS.NREVRANGE requires exactly %d aggregator spec(s), got %d\", len(keys), len(aggregators))\n\t}\n\tparts := make([]string, len(aggregators))\n\tfor i, spec := range aggregators {\n\t\tif len(spec) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"redis: empty timeseries aggregator spec at index %d\", i)\n\t\t}\n\t\tnames := make([]string, len(spec))\n\t\tfor j, agg := range spec {\n\t\t\tif agg == Invalid {\n\t\t\t\treturn nil, fmt.Errorf(\"redis: invalid timeseries aggregator at index %d[%d]: Invalid (%d)\", i, j, agg)\n\t\t\t}\n\t\t\ts := agg.String()\n\t\t\tif s == \"\" {\n\t\t\t\treturn nil, fmt.Errorf(\"redis: invalid timeseries aggregator at index %d[%d]: %d\", i, j, agg)\n\t\t\t}\n\t\t\tnames[j] = s\n\t\t}\n\t\tparts[i] = strings.Join(names, \",\")","sourceCodeStart":1359,"sourceCodeEnd":1395,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/timeseries_commands.go#L1359-L1395","documentation":"Returned by buildNRangeAggregationArgs (used by TS.NRANGE/TS.NREVRANGE via TSNRangeWithArgs/TSNRevRangeWithArgs) when the number of aggregator specs does not equal the number of keys. Each key must receive exactly one comma-joined aggregator spec, so len(aggregators) must equal len(keys).","triggerScenarios":"Calling TSNRangeWithArgs or TSNRevRangeWithArgs with a keys slice of length N but an aggregators slice of length M != N (including M=0 when keys are non-empty and aggregation is requested).","commonSituations":"Building keys and aggregators from different data sources that got out of sync, passing one aggregator spec intending it to apply to all keys, or forgetting to add a spec per key.","solutions":["Pass exactly one []Aggregator per key: len(aggregators) == len(keys).","If the same aggregator applies to all keys, build the slice programmatically: specs := make([][]redis.Aggregator, len(keys)); for i := range specs { specs[i] = []redis.Aggregator{redis.Avg} }.","Add an assertion before the call that the two slice lengths match."],"exampleFix":"// before\nkeys := []string{\"ts1\", \"ts2\"}\nopts := &redis.TSNRangeOptions{Aggregators: [][]redis.Aggregator{{redis.Avg}}}\nclient.TSNRangeWithArgs(ctx, keys, 0, -1, opts)\n\n// after\nkeys := []string{\"ts1\", \"ts2\"}\nopts := &redis.TSNRangeOptions{\n    Aggregators: [][]redis.Aggregator{{redis.Avg}, {redis.Avg}},\n}\nclient.TSNRangeWithArgs(ctx, keys, 0, -1, opts)","handlingStrategy":"validation","validationCode":"func validateNRangeAggregators(keys []string, aggs [][]redis.Aggregator) error {\n    if len(aggs) != len(keys) {\n        return fmt.Errorf(\"need %d aggregator specs for %d keys\", len(keys), len(aggs))\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive aggregator specs from the keys slice so lengths stay in sync.","Add an assertion len(aggregators) == len(keys) before calling TSNRangeWithArgs.","When reusing the same aggregator for all keys, build specs in a loop keyed by keys."],"tags":["timeseries","nrange","aggregator","validation","count-mismatch"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}