vitessio/vitess · error
VT09015
VT09015
Error message
schema tracking required
What it means
VT09015 ('schema tracking required') is raised in Union.predicatePerSource while rewriting a pushed-down predicate for each UNION source. The column offsets map (built from the first SELECT's columns) located a position, but the corresponding select item in that source's SELECT is not a plain *sqlparser.AliasedExpr (e.g. it is a star expansion or other non-aliased item), so the underlying expression cannot be substituted. Vitess panics because this state indicates the query planning phase needed schema/semantic tracking that was not available for this shape of query.
Source
Thrown at go/vt/vtgate/planbuilder/operators/union.go:143
// the expression for the original predicate
predicate = ctx.PredTracker.NewJoinPredicate(jp.Current())
}
predicate = sqlparser.CopyOnRewrite(predicate, nil, func(cursor *sqlparser.CopyOnWriteCursor) {
col, ok := cursor.Node().(*sqlparser.ColName)
if !ok {
return
}
idx, ok := offsets[col.Name.Lowered()]
if !ok {
panic(vterrors.VT13001(fmt.Sprintf("could not find the column '%s' on the UNION", sqlparser.String(col))))
}
sel := u.GetSelectFor(i)
ae, ok := sel.GetColumns()[idx].(*sqlparser.AliasedExpr)
if !ok {
panic(vterrors.VT09015())
}
cursor.Replace(ae.Expr)
}, nil).(sqlparser.Expr)
exprPerSource[i] = predicate
}
return exprPerSource
}
func (u *Union) GetSelectFor(source int) *sqlparser.Select {
src := u.Sources[source]
for {
switch op := src.(type) {
case *Horizon:
return getFirstSelect(op.Query)
case *Route:View on GitHub (pinned to 01a25a7d17)
Solutions
- Rewrite the query to use explicit column lists instead of `*` in the UNION branches (e.g. `SELECT a, b FROM t1 UNION SELECT c, d FROM t2`).
- Simplify the predicate so it references columns that map to plain aliased expressions in all UNION branches.
- If this is hit from valid SQL, file a Vitess issue: it indicates an unsupported plan shape that should be caught earlier with a clearer error.
Example fix
// before: predicate references a UNION column produced by SELECT * SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY k; // after SELECT k, v FROM t1 UNION SELECT k2, v2 FROM t2 ORDER BY k;
Defensive patterns
Strategy: validation
Prevention
- Avoid SELECT * in UNION branches; always list columns explicitly.
- Ensure predicates reference select-list columns that are plain aliased expressions.
- Recover: these are panics converted by Vitess's recover into VT09015 errors — catch and surface the SQL for rewriting.
When it happens
Trigger: Calling Union.AddPredicate (which calls predicatePerSource) when a UNION source's first SELECT at the matched column index contains an item that is not *sqlparser.AliasedExpr — e.g. a `SELECT *` that survived planning, or a NEXT-value expression, at the offset the predicate column resolves to.
Common situations: Queries with UNION whose sources use `*` (star) select lists combined with a WHERE/GROUP BY/HAVING predicate pushed onto the UNION; typically hit during Vitess development or when testing unhandled query shapes, not by end-user misconfiguration.
Related errors
- in-memory row count exceeded allowed limit of %d
- VT13001
- VT13001
- err
- cell mode should be handled by the gateway, not the balancer
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/bc35e35429ef44ff.
Report an issue: GitHub.