vitessio/vitess · error · VitessError
VT09015
VT09015
Error message
VT09015: schema tracking required
What it means
VT09015 signals that the planned DML (UPDATE/DELETE executed via DMLWithInput) requires schema tracking in vtgate. When the target table's primary key is unknown — because schema tracking is disabled, vtgate never learned the table's PrimaryKey — the planner cannot build the input-based DML and raises this error instead. It is a configuration/feature-availability error, not a bug.
Source
Thrown at go/vt/vtgate/planbuilder/operators/phases.go:149
}
func findDMLAboveRoute(ctx *plancontext.PlanningContext, root Operator) Operator {
visitor := func(in Operator, _ semantics.TableSet, isRoot bool) (Operator, *ApplyResult) {
switch op := in.(type) {
case *Delete:
return createDMLWithInput(ctx, op, op.Source, op.DMLCommon)
case *Update:
return createDMLWithInput(ctx, op, op.Source, op.DMLCommon)
}
return in, NoRewrite
}
return BottomUp(root, TableID, visitor, stopAtRoute)
}
func createDMLWithInput(ctx *plancontext.PlanningContext, op, src Operator, in *DMLCommon) (Operator, *ApplyResult) {
if len(in.Target.VTable.PrimaryKey) == 0 {
panic(vterrors.VT09015())
}
dm := &DMLWithInput{}
leftComp := make(sqlparser.ValTuple, 0, len(in.Target.VTable.PrimaryKey))
proj := newAliasedProjection(src)
dm.cols = make([][]*sqlparser.ColName, 1)
for _, col := range in.Target.VTable.PrimaryKey {
colName := sqlparser.NewColNameWithQualifier(col.String(), in.Target.Name)
ctx.SemTable.Recursive[colName] = in.Target.ID
proj.AddColumn(ctx, true, false, aeWrap(colName))
dm.cols[0] = append(dm.cols[0], colName)
leftComp = append(leftComp, colName)
}
dm.Source = proj
var targetTable *Table
_ = Visit(src, func(operator Operator) error {
if tbl, ok := operator.(*Table); ok && tbl.QTable != nil && tbl.QTable.ID == in.Target.ID {View on GitHub (pinned to 01a25a7d17)
Solutions
- Start vtgate with schema tracking enabled (--schema_change_signal-enabled / the schema-tracking flag for your release) so the planner can learn table primary keys
- Ensure the target table actually has a PRIMARY KEY defined in MySQL
- If schema tracking cannot be enabled, rewrite the DML to a form not requiring input-based execution
- Check the release notes/flags for your Vitess version, as the enabling flag naming has changed across versions
Example fix
// before: vtgate started without schema tracking vtgate --topo_implementation etcd2 ... // after vtgate --topo_implementation etcd2 --schema_change_signal_enabled ...
Defensive patterns
Strategy: validation
Validate before calling
// check before issuing DML that requires input-based execution
if (!vtgateFlags.schemaTrackingEnabled) {
throw new Error('enable schema tracking on vtgate before running this DML');
}
// and confirm the table has a primary key
SHOW CREATE TABLE target_table; -- must contain a PRIMARY KEY Type guard
null
Try / catch
try {
await vtgate.execute(session, dml, bindVars);
} catch (e) {
if (String(e.message).includes('VT09015')) {
// restart/enable schema tracking or rewrite the DML
}
throw e;
} Prevention
- Enable schema tracking flags on vtgate in environments running complex DML
- Define PRIMARY KEY on every DML target table
- Verify flags after upgrades (flag names change across releases)
- Avoid DML-through-view patterns unless schema tracking is on
When it happens
Trigger: Executing an UPDATE or DELETE that is expanded to DML-with-input (e.g. DML through a view or on a table needing the input-based plan) while the target VTable has no PrimaryKey recorded because vtgate was started without schema tracking enabled.
Common situations: Deployments with default vtgate flags running such DMLs; enabling views/DML-with-input features without turning on schema tracking; tables defined without a primary key in MySQL, so even with tracking there is no key to use.
Related errors
- --buffer-window must be >= 1s (specified value: %v)
- --buffer-window must be <= --buffer-max-failover-duration: %
- --buffer-size must be >= 1 (specified value: %d)
- --buffer-min-time-between-failovers must be >= 1s (specified
- --buffer-drain-concurrency must be >= 1 (specified value: %d
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/43241c1abb10b281.
Report an issue: GitHub.