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.

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.

Appendix: 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)