go-redis/redis · error

redis: GROUPBY is not allowed when multiple aggregators are

Error message

redis: GROUPBY is not allowed when multiple aggregators are specified

What it means

Returned (set on the cmd) by TSMRangeWithArgs/TSMRevRangeWithArgs when both multiple aggregators are specified (Aggregators slice with >1 entry) AND GroupByLabel or Reducer is set. Redis TS.MRANGE does not support GROUPBY across multiple aggregations in one query; go-redis rejects this combination client-side. multiAggregationCount > 1 combined with a grouping option triggers it.

Source

Thrown at timeseries_commands.go:170

	case StdS:
		return "STD.S"
	case VarP:
		return "VAR.P"
	case VarS:
		return "VAR.S"
	case Twa:
		return "TWA"
	case CountNaN:
		return "COUNTNAN"
	case CountAll:
		return "COUNTALL"
	default:
		return ""
	}
}

var (
	errTSMultiAggregationGroupBy = errors.New("redis: GROUPBY is not allowed when multiple aggregators are specified")
	errTSAggregationConflict     = errors.New("redis: setting both Aggregator and Aggregators is not allowed; use Aggregators instead because Aggregator is deprecated")
	errTSExcludeEmptyGroupBy     = errors.New("redis: EXCLUDEEMPTY is not allowed with GROUPBY")
)

func formatAggregationArgs(aggregator Aggregator, aggregators []Aggregator) (string, int, error) {
	if aggregator != Invalid && len(aggregators) > 0 {
		return "", 0, errTSAggregationConflict
	}
	if len(aggregators) == 0 {
		if aggregator == Invalid {
			return "", 0, nil
		}
		aggregationArg, err := formatAggregatorArg(aggregator)
		if err != nil {
			return "", 0, err
		}
		return aggregationArg, 1, nil
	}

View on GitHub (pinned to 36d97525cd)

Solutions

  1. Choose one: either use multiple aggregators WITHOUT GroupByLabel/Reducer, or use a single aggregator WITH grouping.
  2. If you need grouped multi-aggregation, issue separate TSMRange queries per aggregator and group each.
  3. Set GroupByLabel and Reducer to nil when Aggregators has more than one entry.

Example fix

// before — multiple aggregators + grouping (rejected)
opts := &redis.TSMRangeOptions{
    Aggregators:  []redis.Aggregator{redis.Avg, redis.Max},
    GroupByLabel: "host",
}
client.TSMRangeWithArgs(ctx, from, to, filters, opts)

// after — single aggregator with grouping
opts := &redis.TSMRangeOptions{
    Aggregator:   redis.Avg,
    GroupByLabel: "host",
}
// or drop grouping and keep multiple aggregators
opts := &redis.TSMRangeOptions{
    Aggregators: []redis.Aggregator{redis.Avg, redis.Max},
}
Defensive patterns

Strategy: validation

Validate before calling

func validateTSMRangeOpts(o *redis.TSMRangeOptions) error {
    multi := 0
    if o.Aggregator != redis.Invalid {
        multi = 1
    }
    if len(o.Aggregators) > 0 {
        multi = len(o.Aggregators)
    }
    if multi > 1 && (o.GroupByLabel != nil || o.Reducer != nil) {
        return errors.New("GROUPBY/REDUCE not allowed with multiple aggregators")
    }
    return nil
}

Try / catch

cmd := c.TSMRangeWithArgs(ctx, from, to, filters, opts)
if err := cmd.Err(); err != nil {
    if err.Error() == "redis: GROUPBY is not allowed when multiple aggregators are specified" {
        // drop grouping or reduce to a single aggregator
    }
}

Prevention

When it happens

Trigger: Setting TSMRangeOptions.Aggregators to a slice of 2+ aggregators while also setting GroupByLabel or Reducer. Mixing the new multi-aggregator API with the GROUPBY/REDUCE clustering options.

Common situations: Building dashboards that aggregate multiple metrics (multi-aggregator) and also want label grouping. Migrating from single Aggregator to Aggregators without removing the GroupByLabel/Reducer fields. Config-driven query builders that enable both features simultaneously.

Related errors


AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06). Data as JSON: /data/errors/c6ed14e14c88c55b.json. Report an issue: GitHub.