vitessio/vitess · error
VT03032
VT03032
Error message
the target table %s of the UPDATE is not updatable
What it means
VT03032: the planner rejects UPDATE statements whose target is not a RealTable (e.g. a derived table, subquery, or view Vitess cannot update). MySQL's own error for non-updatable targets is mirrored here — the UPDATE names something that is not a base table.
Source
Thrown at go/vt/vtgate/planbuilder/operators/update.go:310
vTbl: vTbl,
cols: cols,
updList: uList,
}
}
func errIfUpdateNotSupported(ctx *plancontext.PlanningContext, stmt *sqlparser.Update) {
for _, ue := range stmt.Exprs {
tblInfo, err := ctx.SemTable.TableInfoForExpr(ue.Name)
if err != nil {
panic(err)
}
if _, isATable := tblInfo.(*semantics.RealTable); !isATable {
var tblName string
ate := tblInfo.GetAliasedTableExpr()
if ate != nil {
tblName = sqlparser.String(ate)
}
panic(vterrors.VT03032(tblName))
}
}
// Now we check if any of the foreign key columns that are being updated have dependencies on other updated columns.
// This is unsafe, and we currently don't support this in Vitess.
if err := ctx.SemTable.ErrIfFkDependentColumnUpdated(stmt.Exprs); err != nil {
panic(err)
}
}
func createUpdateOperator(ctx *plancontext.PlanningContext, updStmt *sqlparser.Update) (Operator, TargetTable, *sqlparser.Update) {
op := crossJoin(ctx, updStmt.TableExprs)
sqc := &SubQueryBuilder{}
if updStmt.Where != nil {
op = addWherePredsToSubQueryBuilder(ctx, updStmt.Where.Expr, op, sqc)
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Rewrite the UPDATE to target the underlying base table directly.
- If updating a view, update the base tables the view selects from instead.
- Push the statement to MySQL directly if it must update a derived construct (e.g. via a passthrough route).
- Check the vschema so the intended table is defined as a routable real table.
Example fix
// before UPDATE (SELECT * FROM users WHERE id = 1) AS u SET name = 'x'; // after UPDATE users SET name = 'x' WHERE id = 1;
Defensive patterns
Strategy: validation
Validate before calling
// Only pass UPDATEs whose target is a base table
if strings.Contains(strings.ToLower(query), "update (select") {
return errors.New("cannot update a derived table through Vitess")
} Try / catch
res, err := vtgate.Execute(ctx, session, query, vars)
if err != nil && strings.Contains(err.Error(), "not updatable") {
// rewrite to update the base table and retry once
query = rewriteToBaseTable(query)
res, err = vtgate.Execute(ctx, session, query, vars)
} Prevention
- Never issue UPDATE against subqueries or derived tables
- Update views only through their base tables
- Run queries through EXPLAIN to catch unsupported targets early
When it happens
Trigger: UPDATE with SET expressions resolved to a table info that is not *semantics.RealTable — updating a derived table alias, an inline view, or certain routed/virtual tables in errIfUpdateNotSupported.
Common situations: Porting MySQL applications that update derived tables; updating a view that Vitess cannot merge; updating a table through an unsupported routing construct.
Related errors
- VT12001
- VT13001
- VT03015
- invalidUpdateExpr(...)
- both atomic copy and partial mode cannot be specified for th
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/151d8dc8206e5a5a.
Report an issue: GitHub.