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 the

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Remove the immutable column from the UPDATE's SET list and update only mutable columns
  2. Insert a new row instead of mutating an immutable identity column
  3. 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

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


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