vitessio/vitess · error

VT03005

VT03005

Error message

VT03005: cannot group on '%s'

What it means

VT03005 is raised by checkForInvalidGroupingExpressions when a GROUP BY expression contains an aggregate function (detected via ctx.IsAggr during the SQLNode walk). Grouping by an aggregate is semantically invalid in SQL — you cannot group on a value computed by aggregation of the same rows.

Source

Thrown at go/vt/vtgate/planbuilder/operators/queryprojection.go:711

	for _, selectExpr := range qp.SelectExprs {
		getExpr, err := selectExpr.GetExpr()
		if err != nil {
			continue
		}
		if ctx.SemTable.EqualsExprWithDeps(getExpr, expr) {
			return
		}
	}
	qp.SelectExprs = append(qp.SelectExprs, SelectExpr{
		Col:  aeWrap(expr),
		Aggr: ctx.ContainsAggr(expr),
	})
}

func checkForInvalidGroupingExpressions(ctx *plancontext.PlanningContext, expr sqlparser.Expr) {
	_ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) {
		if ctx.IsAggr(node) {
			panic(vterrors.VT03005(sqlparser.String(expr)))
		}
		_, isSubQ := node.(*sqlparser.Subquery)
		arg, isArg := node.(*sqlparser.Argument)
		if isSubQ || (isArg && strings.HasPrefix(arg.Name, "__sq")) {
			panic(vterrors.VT12001("subqueries in GROUP BY"))
		}
		return true, nil
	}, expr)
}

func SortGrouping(a []GroupBy) {
	sort.Slice(a, func(i, j int) bool {
		return CompareRefInt(a[i].InnerIndex, a[j].InnerIndex)
	})
}

// CompareRefInt compares two references of integers.
// In case either one is nil, it is considered to be smaller

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Remove the aggregate from GROUP BY; group on the underlying column instead (e.g. `GROUP BY dept`).
  2. If you meant to filter on an aggregate, move it to HAVING (e.g. `HAVING COUNT(*) > 1`).
  3. Fix the ORM/query builder so it emits valid GROUP BY expressions.

Example fix

// before
SELECT dept, COUNT(*) FROM emp GROUP BY COUNT(*);
// after
SELECT dept, COUNT(*) FROM emp GROUP BY dept HAVING COUNT(*) > 1;
Defensive patterns

Strategy: validation

Validate before calling

// Reject aggregates inside GROUP BY before sending the query
_ = sqlparser.Walk(func(n sqlparser.SQLNode) (bool, error) {
    if isAggregateFunc(n) && insideGroupBy(n) {
        return false, errors.New("GROUP BY cannot contain aggregate functions")
    }
    return true, nil
}, stmt)

Try / catch

if strings.Contains(err.Error(), "VT03005") { /* surface SQL rewrite guidance to caller */ }

Prevention

When it happens

Trigger: GROUP BY clause expression whose tree contains any aggregate node (e.g. `GROUP BY COUNT(x)`, or `GROUP BY SUM(a)+b`).

Common situations: Typos or misunderstandings of GROUP BY semantics, often from ORM-generated SQL or queries translated from other dialects; e.g. `SELECT dept, COUNT(*) FROM emp GROUP BY COUNT(*)`.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/4bd04e486a6e4078. Report an issue: GitHub.