{"record":{"id":"1ec765e795fea207","repo":"vitessio/vitess","slug":"vt13001-1ec765","errorCode":"VT13001","errorMessage":"VT13001: [BUG] should not see %T here","messagePattern":"VT13001: \\[BUG\\] should not see %T here","errorType":"error_code","errorClass":"VitessError","httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/offset_planning.go","lineNumber":38,"sourceCode":"\t\"fmt\"\n\n\t\"vitess.io/vitess/go/vt/sqlparser\"\n\t\"vitess.io/vitess/go/vt/vterrors\"\n\t\"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext\"\n\t\"vitess.io/vitess/go/vt/vtgate/semantics\"\n)\n\ntype offsettable interface {\n\tOperator\n\tplanOffsets(ctx *plancontext.PlanningContext) Operator\n}\n\n// planOffsets will walk the tree top down, adding offset information to columns in the tree for use in further optimization,\nfunc planOffsets(ctx *plancontext.PlanningContext, root Operator) Operator {\n\tvisitor := func(in Operator, _ semantics.TableSet, _ bool) (Operator, *ApplyResult) {\n\t\tswitch op := in.(type) {\n\t\tcase *Horizon:\n\t\t\tpanic(vterrors.VT13001(fmt.Sprintf(\"should not see %T here\", in)))\n\t\tcase offsettable:\n\t\t\tnewOp := op.planOffsets(ctx)\n\t\t\tif newOp == nil {\n\t\t\t\tnewOp = op\n\t\t\t}\n\n\t\t\tif DebugOperatorTree {\n\t\t\t\tfmt.Println(\"Planned offsets for:\")\n\t\t\t\tfmt.Println(ToTree(newOp))\n\t\t\t}\n\n\t\t\tif newOp == op {\n\t\t\t\treturn newOp, nil\n\t\t\t} else {\n\t\t\t\t// We got a new operator from plan offsets. We should return that something has changed.\n\t\t\t\treturn newOp, Rewrote(\"planning offsets introduced a new operator\")\n\t\t\t}\n\t\t}","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/offset_planning.go#L20-L56","documentation":"VT13001 internal-bug panic in planOffsets. Offsets are planned after horizons are merged; encountering a *Horizon operator during the offset-planning walk means a Horizon survived earlier phases when it should have been collapsed. It is an invariant violation indicating a planner bug, not user error.","triggerScenarios":"The offset-planning pass (planOffsets) visits a *Horizon node — i.e. a query whose Horizon was not merged/lowered before offset planning runs (subqueries/nested SELECTs hitting an unhandled path).","commonSituations":"Queries with nested subqueries or layered projections that keep a Horizon alive; regressions after planner upgrades.","solutions":["Capture the failing SQL and stack trace and file a Vitess bug","Rewrite the query (inline subqueries, simplify nested SELECTs) as a workaround","Retry on the latest release where horizon-merging has been reworked"],"exampleFix":"// before\nSELECT * FROM (SELECT * FROM (SELECT id FROM t) a) b\n// after: flatten to a single-level query\nSELECT id FROM t","handlingStrategy":"fallback","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  rows = await vtgate.execute(session, query, bindVars);\n} catch (e) {\n  if (String(e.message).includes('VT13001')) {\n    log.error({query, err: e}, 'planner bug: horizon survived offset planning');\n    // retry with simplified/flattened query\n  }\n  throw e;\n}","preventionTips":["Avoid deeply nested subquery layers","Run planner-intensive queries through staging before upgrades","Report VT13001 panics upstream with minimal repro"],"tags":["vtgate","planbuilder","internal-bug","offset-planning"],"backgroundTag":"internal-bug-panic-vt13001","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}