vitessio/vitess · error
ErrCannotUpdateImmutableColumn
ErrCannotUpdateImmutableColumn
Error message
%w %+v: %v
What it means
For UPDATEs on _vt.vreplication, the planner forbids modifying the immutable `id` column while allowing all other columns. An assignment to `id` is rejected with ErrCannotUpdateImmutableColumn, echoing the column name and the full assignment expression.
Source
Thrown at go/vt/vtctl/workflow/vexec/query_planner.go:224
}, nil
}
func (planner *VReplicationQueryPlanner) planUpdate(upd *sqlparser.Update) (*FixedQueryPlan, error) {
if upd.OrderBy != nil || upd.Limit != nil {
return nil, fmt.Errorf(
"%w: UPDATE must not have explicit ordering (have: %v) or limit clauses (have: %v): %v",
ErrUnsupportedQueryConstruct,
upd.OrderBy,
upd.Limit,
sqlparser.String(upd),
)
}
// For updates on the _vt.vreplication table, we ban updates to the `id`
// column, and allow updates to all other columns.
for _, expr := range upd.Exprs {
if expr.Name.Name.EqualString("id") {
return nil, fmt.Errorf(
"%w %+v: %v",
ErrCannotUpdateImmutableColumn,
expr.Name.Name,
sqlparser.String(expr),
)
}
}
upd.Where = addDefaultWheres(planner, upd.Where)
buf := sqlparser.NewTrackedBuffer(nil)
buf.Myprintf("%v", upd)
return &FixedQueryPlan{
ParsedQuery: buf.ParsedQuery(),
workflow: planner.workflow,
tmc: planner.tmc,
}, nilView on GitHub (pinned to 01a25a7d17)
Solutions
- Remove `id = ...` from the SET clause and update only mutable columns
- If copying rows, enumerate explicit non-id columns instead of SELECT *-style full assignments
- Identify the exact expression from the error message (`sqlparser.String(expr)`) and edit that statement
Example fix
// before UPDATE _vt.vreplication SET id = 42, state = 'Running' WHERE id = 7 // after UPDATE _vt.vreplication SET state = 'Running' WHERE id = 7
Defensive patterns
Strategy: validation
Validate before calling
if upd, ok := stmt.(*sqlparser.Update); ok {
for _, expr := range upd.Exprs {
if expr.Name.Name.EqualString("id") {
return fmt.Errorf("cannot update immutable column id on _vt.vreplication")
}
}
} Type guard
func updatableColumns(upd *sqlparser.Update) []string {
var cols []string
for _, expr := range upd.Exprs {
if !expr.Name.Name.EqualString("id") {
cols = append(cols, expr.Name.Name.String())
}
}
return cols
} Try / catch
plan, err := planner.PlanQuery(ctx, stmt)
if errors.Is(err, vexec.ErrCannotUpdateImmutableColumn) {
return fmt.Errorf("strip id from SET clause and retry: %w", err)
} Prevention
- Never include the `id` column in SET for _vt.vreplication updates
- Enumerate explicit columns instead of building updates from full row copies
- Add a lint/validator that rejects `id` assignments in vreplication UPDATEs before planning
When it happens
Trigger: Calling PlanQuery with an UPDATE on _vt.vreplication whose SET list contains an assignment to the `id` column (upd.Exprs entry with Name.Name == "id").
Common situations: Hand-written maintenance SQL that accidentally includes id in the SET list; ORM or code generator emitting full-row updates including the primary key; bulk UPDATE statements assigning all columns from a source row.
Related errors
- ErrUnsupportedQuery
- ErrUnsupportedQueryConstruct
- --batch-size only allowed when all queries are CREATE TABLE|
- UnescapeID err: invalid input identifier '%s'
- UnescapeID err: unexpected single backtick at position %d in
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/0bbda9f40c25680f.
Report an issue: GitHub.