vitessio/vitess · error
VT13001
VT13001
Error message
err.Error()
What it means
VT13001 is thrown when createDeleteOpWithTarget cannot fetch TableInfo for a DML target table set. Delete-with-input planning resolves each DML target constituent of the statement; if TableInfoFor returns an error (table not found in the semantic table for that TableSet), the planner wraps the underlying message in VT13001 ('table not found in vschema'). It indicates the delete targets a table that semantic analysis could not map.
Source
Thrown at go/vt/vtgate/planbuilder/operators/delete.go:167
if del.Comments != nil {
op = newLockAndComment(op, del.Comments, sqlparser.NoLock)
}
return op
}
// getFirstVindex returns the first Vindex, if available
func getFirstVindex(vTbl *vindexes.BaseTable) vindexes.Vindex {
if len(vTbl.ColumnVindexes) > 0 {
return vTbl.ColumnVindexes[0].Vindex
}
return nil
}
func createDeleteOpWithTarget(ctx *plancontext.PlanningContext, target semantics.TableSet, ignore sqlparser.Ignore) dmlOp {
ti, err := ctx.SemTable.TableInfoFor(target)
if err != nil {
panic(vterrors.VT13001(err.Error()))
}
vTbl := ti.GetVindexTable()
if len(vTbl.PrimaryKey) == 0 {
panic(vterrors.VT09015())
}
tblName, err := ti.Name()
if err != nil {
panic(err)
}
leftComp := make(sqlparser.ValTuple, 0, len(vTbl.PrimaryKey))
cols := make([]*sqlparser.ColName, 0, len(vTbl.PrimaryKey))
for _, col := range vTbl.PrimaryKey {
colName := sqlparser.NewColNameWithQualifier(col.String(), tblName)
cols = append(cols, colName)
leftComp = append(leftComp, colName)
ctx.SemTable.Recursive[colName] = targetView on GitHub (pinned to 01a25a7d17)
Solutions
- Read the VT13001 message to identify the unresolved table and confirm it exists in the target keyspace's vschema.
- Rewrite the multi-table DELETE as separate single-table DELETE statements.
- If FK-driven input planning is the trigger, remove the LIMIT clause or restructure so delete-with-input planning isn't required.
- Refresh/redeploy the vschema if the table was recently added or renamed.
Example fix
// before DELETE orders, order_items FROM orders JOIN order_items ON orders.id = order_items.order_id WHERE orders.created_at < '2024-01-01'; // after DELETE FROM orders WHERE created_at < '2024-01-01'; DELETE FROM order_items WHERE order_id NOT IN (SELECT id FROM orders);
Defensive patterns
Strategy: validation
Validate before calling
// validate all targets of a multi-table DELETE resolve before executing
for _, target := range deleteTargets(sql) {
if !keyspaceHasTable(defaultKeyspace, target) {
return fmt.Errorf("VT13001 guard: target %s not found in keyspace %s", target, defaultKeyspace)
}
} Type guard
func isVT13001(err error) bool {
return strings.Contains(err.Error(), "VT13001")
} Try / catch
if err := execDelete(sql); err != nil {
if isVT13001(err) {
log.Warn("delete target not found; checking vschema", slog.Any("error", err))
return refreshVschemaAndRetry(sql)
}
return err
} Prevention
- Prefer single-table DELETE statements
- Avoid LIMIT on deletes of FK-related tables
- Verify vschema after any table rename/migration before running DML
- Monitor for VT13001 codes in app logs to catch stale vschema early
When it happens
Trigger: Multi-table DELETE (e.g. DELETE t1, t2 FROM ...) or DELETE with LIMIT on FK-related tables triggers delete-with-input planning; for one of the SemTable.DMLTargets constituents, ctx.SemTable.TableInfoFor(target) fails.
Common situations: Multi-table deletes referencing tables not present/authorized in the keyspace vschema; typos in qualified names inside multi-table DELETE syntax; stale vschema after a table rename during a rolling migration.
Related errors
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/0c7a57002103954d.
Report an issue: GitHub.