{"record":{"id":"bc35e35429ef44ff","repo":"vitessio/vitess","slug":"vt09015-bc35e3","errorCode":"VT09015","errorMessage":"schema tracking required","messagePattern":"schema tracking required","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/union.go","lineNumber":143,"sourceCode":"\t\t\t// the expression for the original predicate\n\t\t\tpredicate = ctx.PredTracker.NewJoinPredicate(jp.Current())\n\t\t}\n\n\t\tpredicate = sqlparser.CopyOnRewrite(predicate, nil, func(cursor *sqlparser.CopyOnWriteCursor) {\n\t\t\tcol, ok := cursor.Node().(*sqlparser.ColName)\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tidx, ok := offsets[col.Name.Lowered()]\n\t\t\tif !ok {\n\t\t\t\tpanic(vterrors.VT13001(fmt.Sprintf(\"could not find the column '%s' on the UNION\", sqlparser.String(col))))\n\t\t\t}\n\n\t\t\tsel := u.GetSelectFor(i)\n\t\t\tae, ok := sel.GetColumns()[idx].(*sqlparser.AliasedExpr)\n\t\t\tif !ok {\n\t\t\t\tpanic(vterrors.VT09015())\n\t\t\t}\n\n\t\t\tcursor.Replace(ae.Expr)\n\t\t}, nil).(sqlparser.Expr)\n\n\t\texprPerSource[i] = predicate\n\t}\n\n\treturn exprPerSource\n}\n\nfunc (u *Union) GetSelectFor(source int) *sqlparser.Select {\n\tsrc := u.Sources[source]\n\tfor {\n\t\tswitch op := src.(type) {\n\t\tcase *Horizon:\n\t\t\treturn getFirstSelect(op.Query)\n\t\tcase *Route:","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/union.go#L125-L161","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: predicate references a UNION column produced by SELECT *\nSELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY k;\n// after\nSELECT k, v FROM t1 UNION SELECT k2, v2 FROM t2 ORDER BY k;","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["query-planning","union","vtgate","internal-panic"],"backgroundTag":"union-star-column-pushdown","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}