vitessio/vitess · error
VT12002
VT12002
Error message
VT12002(sqlparser.String(tblName), fk.Table.String())
What it means
VT12002 (unsupported: cross-shard foreign key constraint) is raised when a DELETE on a parent table has a child foreign key with ON DELETE RESTRICT that Vitess must emulate itself — specifically the cross-shard/cross-keyspace RESTRICT cases that reach createFkCascadeOpForDelete. Since RESTRICT semantics (reject if children exist) cannot be enforced efficiently across shards in this code path, the planner rejects the delete.
Source
Thrown at go/vt/vtgate/planbuilder/operators/delete.go:351
tbl.Table, _ = tbl.Alias.TableName()
}
return op, Rewrote("change query table point to source table")
}, func(operator Operator) VisitRule {
_, ok := operator.(*QueryGraph)
return VisitRule(ok)
})
return vTbl
}
func createFkCascadeOpForDelete(ctx *plancontext.PlanningContext, parentOp Operator, delStmt *sqlparser.Delete, childFks []vindexes.ChildFKInfo, deletedTbl *vindexes.BaseTable) Operator {
fkChildren := make([]*FkChild, 0, len(childFks))
var selectExprs []sqlparser.SelectExpr
tblName := delStmt.Targets[0]
for _, fk := range childFks {
// Any RESTRICT type foreign keys that arrive here,
// are cross-shard/cross-keyspace RESTRICT cases, which we don't currently support.
if fk.OnDelete.IsRestrict() {
panic(vterrors.VT12002(sqlparser.String(tblName), fk.Table.String()))
}
// We need to select all the parent columns for the foreign key constraint, to use in the update of the child table.
var offsets []int
offsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs, tblName)
fkChildren = append(fkChildren,
createFkChildForDelete(ctx, fk, offsets))
}
selectionOp := createSelectionOp(ctx, selectExprs, delStmt.TableExprs, delStmt.Where, nil, nil, getUpdateLock(deletedTbl))
return &FkCascade{
Selection: selectionOp,
Children: fkChildren,
Parent: parentOp,
}
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Change the FK to ON DELETE CASCADE or ON DELETE SET NULL if business logic allows, so Vitess can emulate it.
- Enforce the delete guard in application logic: check for and delete/NULL child rows before deleting the parent.
- Co-locate parent and child tables in the same keyspace/shard (via VReplication move or redesign) so MySQL can enforce RESTRICT natively.
- Set foreign_key_mode appropriately (e.g. `disallow` to reject cross-shard FK DDL up front) so the situation is surfaced during migration rather than at delete time.
Example fix
// before CREATE TABLE child (id INT, parent_id INT, FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE RESTRICT); // after CREATE TABLE child (id INT, parent_id INT, FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE);
Defensive patterns
Strategy: validation
Validate before calling
-- detect unsupported RESTRICT child FKs on a parent table before issuing DELETE SELECT child.table_name AS child_table FROM information_schema.referential_constraints rc JOIN information_schema.key_column_usage kcu ON kcu.constraint_name = rc.constraint_name WHERE rc.unique_constraint_schema = ? AND rc.referenced_table_name = ? AND rc.delete_rule = 'RESTRICT';
Type guard
func fkDeleteIsSupported(fk vindexes.ChildFKInfo) bool {
return !fk.OnDelete.IsRestrict()
} Try / catch
if err := execDelete(sql); err != nil {
if strings.Contains(err.Error(), "VT12002") {
return fmt.Errorf("cross-shard RESTRICT FK blocks this DELETE; delete child rows first or use CASCADE: %w", err)
}
return err
} Prevention
- Prefer ON DELETE CASCADE or SET NULL in sharded schemas
- Delete child rows explicitly before parent rows when RESTRICT is required
- Avoid cross-keyspace foreign keys in sharded deployments
- Choose the right --foreign_key_mode at deployment and enforce it in schema review
When it happens
Trigger: DELETE from a parent table that has a child FK with `ON DELETE RESTRICT` where parent and child are in different keyspaces or the FK is managed by Vitess (managed foreign key mode) rather than pushed down to MySQL.
Common situations: Applications with legacy schemas using ON DELETE RESTRICT migrated to sharded Vitess; cross-keyspace FK relationships; enabling `--foreign_key_mode=managed`/`unmanaged` boundaries where RESTRICT cannot be honored sharded.
Related errors
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/77f86a46da7f822a.
Report an issue: GitHub.