vitessio/vitess · error

VT09002

VT09002

Error message

update

What it means

VT09002 signals an unsupported feature in Vitess. When planning an UPDATE statement routed to an explicit target (e.g. `@primary`, `@replica`, a keyspace:shard target), the target tablet type must be PRIMARY; UPDATE cannot be executed against replica targets.

Source

Thrown at go/vt/vtgate/planbuilder/operators/route.go:407

		panic(err)
	}

	targeted := createTargetedRouting(ctx, target, tabletType, vschemaTable)

	return createRouteFromVSchemaTable(
		ctx,
		queryTable,
		vschemaTable,
		planAlternates,
		targeted,
	)
}

func createTargetedRouting(ctx *plancontext.PlanningContext, target key.ShardDestination, tabletType topodatapb.TabletType, vschemaTable *vindexes.BaseTable) Routing {
	switch ctx.Statement.(type) {
	case *sqlparser.Update:
		if tabletType != topodatapb.TabletType_PRIMARY {
			panic(vterrors.VT09002("update"))
		}
	case *sqlparser.Delete:
		if tabletType != topodatapb.TabletType_PRIMARY {
			panic(vterrors.VT09002("delete"))
		}
	case *sqlparser.Insert:
		if tabletType != topodatapb.TabletType_PRIMARY {
			panic(vterrors.VT09002("insert"))
		}
		if target != nil {
			panic(vterrors.VT09017("INSERT with a target destination is not allowed"))
		}
	case sqlparser.SelectStatement:
		if target != nil {
			panic(vterrors.VT09017("SELECT with a target destination is not allowed"))
		}
	}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Issue the UPDATE against the PRIMARY target (e.g. `USE ks@primary` or remove the target qualifier)
  2. Reset the session target back to primary before running DML
  3. Fix application routing logic so writes never target replicas

Example fix

// before
USE ks@replica;
UPDATE t SET a = 1 WHERE id = 5; -- VT09002: update
// after
USE ks@primary;
UPDATE t SET a = 1 WHERE id = 5;
Defensive patterns

Strategy: validation

Validate before calling

-- check current target before DML
SELECT @@vt_target; -- or inspect session target in the driver
// issue DML only when target is primary
if currentTarget != "ks@primary" { conn.Execute("USE ks@primary", nil) }

Try / catch

_, err := conn.Execute("UPDATE t SET a=1 WHERE id=5", nil)
if err != nil && strings.Contains(err.Error(), "VT09002") {
    conn.Execute("USE ks@primary", nil)
    // retry the statement
}

Prevention

When it happens

Trigger: Executing an UPDATE statement with a targeted destination (USE with a target like keyspace@replica, or `@replica`/`@rdonly`/shard target) so createTargetedRouting receives a non-PRIMARY tabletType.

Common situations: Session was switched to a replica/rdonly target (`USE ks@replica`) and the application then issues an UPDATE; misconfigured routing rules pointing DML at non-primary tablets.

Related errors


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