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

  1. Rewrite the UPDATE to target the underlying base table directly.
  2. If updating a view, update the base tables the view selects from instead.
  3. Push the statement to MySQL directly if it must update a derived construct (e.g. via a passthrough route).
  4. 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

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


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