vitessio/vitess · error
VT12001
VT12001
Error message
VT12001: unsupported: in scatter query: complex aggregate expression
What it means
VT12001 is raised when AggregationExpressions encounters an aggregate expression that is neither a simple aggregate nor permitted as a complex expression, in a scatter query. Scatter queries aggregate results across shards, so each output column must be a directly pushable aggregate; arbitrary complex expressions over aggregates cannot be computed correctly from partial per-shard results.
Source
Thrown at go/vt/vtgate/planbuilder/operators/queryprojection.go:442
if !ctx.ContainsAggr(selectExpr.Col) {
getExpr, err := selectExpr.GetExpr()
if err != nil {
panic(err)
}
if ctx.ContainsWindowFunc(selectExpr.Col) {
sqlparser.CopyOnRewrite(aliasedExpr.Expr, qp.extractAggr(ctx, aliasedExpr, addAggr, makeComplex), nil, nil)
continue
}
if !qp.isExprInGroupByExprs(ctx, getExpr) {
aggr := createNonGroupingAggr(aliasedExpr)
out = append(out, aggr)
}
continue
}
if !ctx.IsAggr(aliasedExpr.Expr) && !allowComplexExpression {
panic(vterrors.VT12001("in scatter query: complex aggregate expression"))
}
sqlparser.CopyOnRewrite(aliasedExpr.Expr, qp.extractAggr(ctx, aliasedExpr, addAggr, makeComplex), nil, nil)
}
return
}
func (qp *QueryProjection) extractAggr(
ctx *plancontext.PlanningContext,
aliasedExpr *sqlparser.AliasedExpr,
addAggr func(a Aggr),
makeComplex func(),
) func(node sqlparser.SQLNode, parent sqlparser.SQLNode) bool {
return func(node, parent sqlparser.SQLNode) bool {
ex, isExpr := node.(sqlparser.Expr)
if !isExpr {
return true
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Rewrite the query so each aggregate is a simple top-level function, e.g. `SELECT SUM(a), SUM(b) FROM t` and compute `SUM(a)-SUM(b)` in the application.
- Add a predicate on the sharding key (e.g. `WHERE sharding_key = ?`) so the query routes to a single shard instead of scattering.
- Restructure the VSchema/sharding or use an unsharded keyspace if the aggregate pattern is common.
Example fix
// before SELECT SUM(price) / COUNT(*) AS avg_price FROM orders; // after SELECT SUM(price) AS total, COUNT(*) AS cnt FROM orders; // divide client-side
Defensive patterns
Strategy: try-catch
Validate before calling
// Reject complex arithmetic over aggregates for scatter routes before sending
if isScatterRoute(query) && containsNestedAggregateExpr(query) {
return errors.New("rewrite query: scatter queries only support simple top-level aggregates")
} Try / catch
if strings.Contains(err.Error(), "VT12001") && strings.Contains(err.Error(), "scatter") {
// fall back to splitting aggregates client-side or route with sharding key
} Prevention
- Keep aggregate SELECTs to simple top-level aggregate functions
- Add sharding-key predicates to avoid scatter routes
- Compute post-aggregate arithmetic in the application
When it happens
Trigger: createProjectionWithAggr calls AggregationExpressions with allowComplexExpression=false for a scatter-route query whose SELECT contains a complex expression mixing aggregates with other expressions (e.g. `SUM(x)/COUNT(y)` composed with extra terms the planner can't push down).
Common situations: Aggregates over sharded tables without a usable sharding-key predicate (forcing scatter), combined with complex expressions like arithmetic on aggregates or nested functions, e.g. `SELECT SUM(a) - SUM(b) FROM t` on a scatter.
Related errors
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/f5a98864c9e3a38c.
Report an issue: GitHub.