vitessio/vitess · error · ErrCannotUpdateImmutableColumn
cannot update immutable column
Error message
cannot update immutable column
What it means
ErrCannotUpdateImmutableColumn is a sentinel error returned by the VReplication query planner (planUpdate) when an UPDATE statement targets a column that must never be mutated during vreplication vexec operations (e.g. identity/immutable bookkeeping columns). The planner refuses to build such a plan. Tests reference it directly, so it is part of the planner's public contract.
Source
Thrown at go/vt/vtctl/workflow/vexec/query_planner.go:31
See the License for the specific language governing permissions and
limitations under the License.
*/
package vexec
import (
"errors"
"fmt"
"strconv"
"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vttablet/tmclient"
)
var ( // Query planning errors.
// ErrCannotUpdateImmutableColumn is returned when attempting to plan a
// query that updates a column that should be treated as immutable.
ErrCannotUpdateImmutableColumn = errors.New("cannot update immutable column")
// ErrUnsupportedQueryConstruct is returned when a particular query
// construct is unsupported by a QueryPlanner, despite the more general kind
// of query being supported.
//
// For example, VReplication supports DELETEs, but does not support DELETEs
// with LIMIT clauses, so planning a "DELETE ... LIMIT" will return
// ErrUnsupportedQueryConstruct rather than a "CREATE TABLE", which would
// return an ErrUnsupportedQuery.
ErrUnsupportedQueryConstruct = errors.New("unsupported query construct")
)
// Query execution errors.
// ErrUnpreparedQuery is returned when attempting to execute an unprepared
// QueryPlan.
var ErrUnpreparedQuery = errors.New("attempted to execute unprepared query")
// QueryPlanner defines the interface that VExec uses to build QueryPlans for
// various vexec workflows. A given vexec table, which is to say a table in theView on GitHub (pinned to 01a25a7d17)
Solutions
- Remove the immutable column from the UPDATE's SET list and update only mutable columns
- Insert a new row instead of mutating an immutable identity column
- Use the intended workflow control path (MoveTablesComplete, Workflow Stop/Start) instead of raw UPDATEs on _vt tables
Example fix
// before UPDATE _vt.vreplication SET id = 2 WHERE id = 1 // ErrCannotUpdateImmutableColumn // after UPDATE _tv.vreplication SET state = 'Running', message = '' WHERE id = 1
Defensive patterns
Strategy: type-guard
Validate before calling
for _, col := range updateStmt.SetColumns() {
if immutableColumns[table.Contains(col)] {
return ErrCannotUpdateImmutableColumn
}
} Type guard
func isImmutableColumn(c sqlparser.ColName) bool {
return c.Metadata != nil && c.Metadata.IsIdentity
} Try / catch
err := planner.PlanUpdate(stmt)
if errors.Is(err, workflow.ErrCannotUpdateImmutableColumn) {
return fmt.Errorf("rewrite UPDATE without setting immutable columns: %w", err)
} Prevention
- Never UPDATE identity/primary-key columns of _vt tables
- Use workflow control commands instead of raw SQL on _vt.vreplication
- Enumerate immutable columns in your tooling's allow-list of updatable columns
When it happens
Trigger: Planning a VExec UPDATE against a _vt table whose plan (QueryPlanner.planUpdate) detects a SET clause on a column classified as immutable (e.g. a primary-key/identity column of the vreplication state tables).
Common situations: Hand-written VExec SQL via vtctldclient trying to patch vreplication rows' key columns, or tooling that generates UPDATE statements without excluding immutable columns.
Related errors
- unsupported query construct
- attempted to execute unprepared query
- no primary found for shard
- table not supported by vexec
- value out of range
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/5969ec3c0667492f.
Report an issue: GitHub.