vitessio/vitess · error

VT12001

VT12001

Error message

ON DUPLICATE KEY UPDATE with foreign keys with unique keys

What it means

INSERT ... ON DUPLICATE KEY UPDATE against a table that has foreign keys and declared unique keys is unsupported. The upsert planning path requires PK comparison and cannot safely handle unique-key interactions with FK verification, so it panics with VT12001.

Source

Thrown at go/vt/vtgate/planbuilder/operators/upsert.go:82

	return inputs
}

func (u *Upsert) SetInputs(inputs []Operator) {
	u.Sources = nil
	u.setInputs(inputs)
}

func (u *Upsert) ShortDescription() string {
	return ""
}

func (u *Upsert) GetOrdering(ctx *plancontext.PlanningContext) []OrderBy {
	return nil
}

func createUpsertOperator(ctx *plancontext.PlanningContext, ins *sqlparser.Insert, insOp Operator, rows sqlparser.Values, vTbl *vindexes.BaseTable) Operator {
	if len(vTbl.UniqueKeys) != 0 {
		panic(vterrors.VT12001("ON DUPLICATE KEY UPDATE with foreign keys with unique keys"))
	}

	pIndexes, _ := findPKIndexes(vTbl, ins)
	if len(pIndexes) == 0 {
		// nothing to compare for update.
		// Hence, only perform insert.
		return insOp
	}

	upsert := &Upsert{}
	for _, row := range rows {
		var comparisons []sqlparser.Expr
		for _, pIdx := range pIndexes {
			var expr sqlparser.Expr
			if pIdx.idx == -1 {
				expr = pIdx.def
			} else {
				expr = row[pIdx.idx]

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Remove the extra unique keys from the table, keeping only the primary key
  2. Perform the insert/update logic in the application: SELECT then INSERT or UPDATE explicitly
  3. Avoid managed FK mode for this table, or restructure so the upsert targets a table without unique keys
  4. If the unique key is unnecessary for correctness, drop it

Example fix

// before
CREATE TABLE t (id INT PRIMARY KEY, email VARCHAR(64), UNIQUE KEY uq_email (email));
INSERT INTO t ... ON DUPLICATE KEY UPDATE ...;
// after
CREATE TABLE t (id INT PRIMARY KEY, email VARCHAR(64)); -- upsert handled in app or unique key dropped
Defensive patterns

Strategy: validation

Validate before calling

// before using upsert on a table with managed FKs
if len(table.UniqueKeys) > 0 && usesOnDuplicateKeyUpdate(stmt) {
    return fmt.Errorf("upsert unsupported for %s: table has unique keys with FKs", table.Name)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "ON DUPLICATE KEY UPDATE with foreign keys with unique keys") {
    // fall back to app-level select-then-insert/update
}

Prevention

When it happens

Trigger: createUpsertOperator is invoked for a table where vTbl.UniqueKeys is non-empty (table participates in FKs and has unique keys beyond the primary key) and the statement is an upsert.

Common situations: Tables with UNIQUE constraints used in upsert-heavy workloads after enabling managed foreign keys; migrating MySQL upserts to Vitess without revisiting unique indexes.

Related errors


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