{"record":{"id":"b6002ef9b4051e4c","repo":"vitessio/vitess","slug":"vt13001-b6002e","errorCode":"VT13001","errorMessage":"could not find the column '%s' on the UNION","messagePattern":"could not find the column '(.+?)' on the UNION","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/union.go","lineNumber":137,"sourceCode":"\tfor i := range u.Sources {\n\t\tpredicate := expr\n\n\t\tif jp, ok := predicate.(*predicates.JoinPredicate); ok {\n\t\t\t// Create a new JoinPredicate for each source to keep tracking working\n\t\t\t// We can't use `*JoinPredicate.Clone` here as that would update the tracker and overwrite\n\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 {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/union.go#L119-L155","documentation":"VT13001 signals an internal planner programming error. While distributing a predicate to each UNION source, the planner looks up the predicate's column name in the offsets map built from the first SELECT's columns; if a source's column cannot be found there, the planner's column-mapping invariant is broken and it panics instead of emitting a wrong plan. The message includes the missing column name.","triggerScenarios":"During Union.AddPredicate / predicatePerSource, a ColName in the pushed expression is not present (case-insensitively) in the offsets map built from the first SELECT's column list — e.g. the predicate references a column that the UNION does not actually output, or a column-name mismatch between UNION arms.","commonSituations":"Queries whose WHERE clause on a UNION references a column missing from the first SELECT, or planner regressions where offsets were built before query normalization/aliasing (e.g. unmatched case or renamed output columns).","solutions":["Verify the column named in the panic message ('could not find the column %s on the UNION') actually appears in the first SELECT's output list, and add it if not.","Use explicit, matching column names/aliases in all UNION arms so the predicate column resolves cleanly.","Minimize the query and check against the latest Vitess version; if a valid query triggers this, file a planner bug with the query and stack trace."],"exampleFix":"// before: predicate references a column not in the union output\nSELECT id FROM a UNION SELECT x FROM b) WHERE name = 'x'\n\n// after: ensure the column is selected\nSELECT id, name FROM a UNION SELECT x, y AS name FROM b) WHERE name = 'x'","handlingStrategy":"validation","validationCode":"// ensure every predicate column exists in the union's first SELECT output\nwant := strings.ToLower(colName)\nfound := false\nfor _, se := range unionOp.GetSelectFor(0).GetColumns() {\n    if ae, ok := se.(*sqlparser.AliasedExpr); ok && strings.ToLower(ae.ColumnName()) == want {\n        found = true\n    }\n}\nif !found {\n    return errors.New(\"predicate column missing from UNION output\")\n}","typeGuard":"func columnInUnion(u *operators.Union, col string) bool {\n    for _, se := range u.GetSelectFor(0).GetColumns() {\n        if ae, ok := se.(*sqlparser.AliasedExpr); ok {\n            if strings.ToLower(ae.ColumnName()) == strings.ToLower(col) {\n                return true\n            }\n        }\n    }\n    return false\n}","tryCatchPattern":"// Internal invariant panic; pre-validate columns. If a recovery boundary exists:\ndefer func() {\n    if r := recover(); r != nil {\n        if vte, ok := r.(error); ok && vterrors.Code(vte) == vtrpcpb.Code_VT13001 {\n            // treat as planner bug: log query + stack, re-panic or return internal error\n        }\n    }\n}()","preventionTips":["Ensure WHERE columns on a UNION are present in the first SELECT's output list.","Give explicit aliases to all UNION arm columns so name matching is deterministic.","Minimize and test any query that triggers VT13001 on a valid SQL shape and report it as a planner bug."],"tags":["planbuilder","union","internal-error","predicate-pushdown"],"backgroundTag":"unsupported-operator-method","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}