vitessio/vitess · error · VitessError

VT13001

VT13001

Error message

VT13001: [BUG] unhandled default case

What it means

VT13001 internal-bug panic in Phase.String(). The method enumerates all known planning phases; a Phase value outside the enumerated cases (uninitialized or future/foreign value) hits the default and panics. It exists purely to catch programming errors when a new phase is added without updating String.

Source

Thrown at go/vt/vtgate/planbuilder/operators/phases.go:68

		return "physicalTransform"
	case initialPlanning:
		return "initial horizon planning optimization"
	case pullDistinctFromUnion:
		return "pull distinct from UNION"
	case delegateAggregation:
		return "split aggregation between vtgate and mysql"
	case recursiveCTEHorizons:
		return "expand recursive CTE horizons"
	case addAggrOrdering:
		return "optimize aggregations with ORDER BY"
	case cleanOutPerfDistinct:
		return "optimize Distinct operations"
	case subquerySettling:
		return "settle subqueries"
	case dmlWithInput:
		return "expand update/delete to dml with input"
	default:
		panic(vterrors.VT13001("unhandled default case"))
	}
}

func (p Phase) shouldRun(s semantics.QuerySignature) bool {
	switch p {
	case pullDistinctFromUnion:
		return s.Union
	case delegateAggregation:
		return s.Aggregation
	case recursiveCTEHorizons:
		return s.RecursiveCTE
	case addAggrOrdering:
		return s.Aggregation
	case cleanOutPerfDistinct:
		return s.Distinct
	case subquerySettling:
		return s.SubQueries
	case dmlWithInput:

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Add the missing Phase case to the String() switch in go/vt/vtgate/planbuilder/operators/phases.go
  2. File a bug with the stack trace if this appears without local code changes
  3. Run on the latest release where all phases are covered

Example fix

// before
case dmlWithInput:
    return "expand update/delete to dml with input"
default:
    panic(vterrors.VT13001("unhandled default case"))
// after: add the new phase before default
case myNewPhase:
    return "my new phase"
default:
    panic(vterrors.VT13001("unhandled default case"))
Defensive patterns

Strategy: type-guard

Validate before calling

// Vitess contributors: ensure every Phase constant has a String case
for (const p of allPhases) {
  if (phaseToString(p) === undefined) throw new Error('missing String case for phase ' + p);
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling String() (e.g. via logging/tracing a phase) on a Phase value not covered by the switch — typically a newly added phase constant whose String case was forgotten, or a zero-value/uninitialized Phase.

Common situations: Development of Vitess itself (adding a phase to phases.go without updating String); end users only see it via a planner bug's stack trace in logs.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/98d9fd78011438e6. Report an issue: GitHub.