{"record":{"id":"8c25788c3af63b10","repo":"vitessio/vitess","slug":"vt13001-8c2578","errorCode":"VT13001","errorMessage":"should not happen: we should be able to merge cross joins","messagePattern":"should not happen: we should be able to merge cross joins","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/route_planning.go","lineNumber":220,"sourceCode":"\tif len(physicalOps) == 0 {\n\t\treturn nil\n\t}\n\tfor len(physicalOps) > 1 {\n\t\tbestTree, lIdx, rIdx := findBestJoin(ctx, qg, physicalOps, planCache, crossJoinsOK)\n\t\t// if we found a plan, we'll replace the two plans that were joined with the join plan created\n\t\tif bestTree != nil {\n\t\t\t// we remove one plan, and replace the other\n\t\t\tif rIdx > lIdx {\n\t\t\t\tphysicalOps = removeAt(physicalOps, rIdx)\n\t\t\t\tphysicalOps = removeAt(physicalOps, lIdx)\n\t\t\t} else {\n\t\t\t\tphysicalOps = removeAt(physicalOps, lIdx)\n\t\t\t\tphysicalOps = removeAt(physicalOps, rIdx)\n\t\t\t}\n\t\t\tphysicalOps = append(physicalOps, bestTree)\n\t\t} else {\n\t\t\tif crossJoinsOK {\n\t\t\t\tpanic(vterrors.VT13001(\"should not happen: we should be able to merge cross joins\"))\n\t\t\t}\n\t\t\t// we will only fail to find a join plan when there are only cross joins left\n\t\t\t// when that happens, we switch over to allow cross joins as well.\n\t\t\t// this way we prioritize joining physicalOps with predicates first\n\t\t\tcrossJoinsOK = true\n\t\t}\n\t}\n\treturn physicalOps[0]\n}\n\nfunc removeAt(plans []Operator, idx int) []Operator {\n\treturn append(plans[:idx], plans[idx+1:]...)\n}\n\nfunc findBestJoin(\n\tctx *plancontext.PlanningContext,\n\tqg *QueryGraph,\n\tplans []Operator,","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/route_planning.go#L202-L238","documentation":"VT13001 panic raised in mergeRoutes when the greedy route planner fails to merge two routes into a join even though cross joins were expected to be mergeable at that point. The planner first tries to merge only routes connected by predicates; it flips crossJoinsOK to true when nothing mergeable remains, so hitting the panic means an internal invariant was broken. This indicates a planner bug, not a user query problem.","triggerScenarios":"greedySolve calls mergeRoutes with a pair of physical operators that could not be merged while crossJoinsOK was false; on a later iteration the planner expects cross joins to now be mergeable but no bestTree can be produced for the remaining pair.","commonSituations":"Encountered during Vitess development or when running unreleased planner changes; occasionally reported by users running unusual queries (e.g. exotic cross joins, derived tables, or new operator types) that expose planner invariants.","solutions":["File a bug with the failing SQL query and full panic stack trace at the Vitess repository","Check the Vitess version and try the latest patch release, since planner merge bugs are frequently fixed","Rewrite the query to include an explicit join predicate between the tables instead of relying on a cross join","As a temporary workaround, split the query into multiple simpler queries executed separately"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// vtgate returns the panic as an error to the client\nrows, err := db.Query(sql)\nif err != nil && strings.Contains(err.Error(), \"VT13001\") {\n    // retry with a rewritten query avoiding the cross join\n    rows, err = db.Query(rewrittenSQL)\n}","preventionTips":["Keep Vitess up to date; planner merge bugs are fixed in patch releases","Test new query shapes against your Vitess version before production rollout","Prefer explicit join predicates over bare cross joins","Include the failing SQL and stack trace when reporting planner panics"],"tags":["planner","query-planning","internal-panic","cross-join"],"backgroundTag":"planner-internal-invariant","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}