{"record":{"id":"5cc2fac60c5e7858","repo":"vitessio/vitess","slug":"vt09002","errorCode":"VT09002","errorMessage":"update","messagePattern":"update","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/planbuilder/operators/route.go","lineNumber":407,"sourceCode":"\t\tpanic(err)\n\t}\n\n\ttargeted := createTargetedRouting(ctx, target, tabletType, vschemaTable)\n\n\treturn createRouteFromVSchemaTable(\n\t\tctx,\n\t\tqueryTable,\n\t\tvschemaTable,\n\t\tplanAlternates,\n\t\ttargeted,\n\t)\n}\n\nfunc createTargetedRouting(ctx *plancontext.PlanningContext, target key.ShardDestination, tabletType topodatapb.TabletType, vschemaTable *vindexes.BaseTable) Routing {\n\tswitch ctx.Statement.(type) {\n\tcase *sqlparser.Update:\n\t\tif tabletType != topodatapb.TabletType_PRIMARY {\n\t\t\tpanic(vterrors.VT09002(\"update\"))\n\t\t}\n\tcase *sqlparser.Delete:\n\t\tif tabletType != topodatapb.TabletType_PRIMARY {\n\t\t\tpanic(vterrors.VT09002(\"delete\"))\n\t\t}\n\tcase *sqlparser.Insert:\n\t\tif tabletType != topodatapb.TabletType_PRIMARY {\n\t\t\tpanic(vterrors.VT09002(\"insert\"))\n\t\t}\n\t\tif target != nil {\n\t\t\tpanic(vterrors.VT09017(\"INSERT with a target destination is not allowed\"))\n\t\t}\n\tcase sqlparser.SelectStatement:\n\t\tif target != nil {\n\t\t\tpanic(vterrors.VT09017(\"SELECT with a target destination is not allowed\"))\n\t\t}\n\t}\n","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/planbuilder/operators/route.go#L389-L425","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Issue the UPDATE against the PRIMARY target (e.g. `USE ks@primary` or remove the target qualifier)","Reset the session target back to primary before running DML","Fix application routing logic so writes never target replicas"],"exampleFix":"// before\nUSE ks@replica;\nUPDATE t SET a = 1 WHERE id = 5; -- VT09002: update\n// after\nUSE ks@primary;\nUPDATE t SET a = 1 WHERE id = 5;","handlingStrategy":"validation","validationCode":"-- check current target before DML\nSELECT @@vt_target; -- or inspect session target in the driver\n// issue DML only when target is primary\nif currentTarget != \"ks@primary\" { conn.Execute(\"USE ks@primary\", nil) }","typeGuard":null,"tryCatchPattern":"_, err := conn.Execute(\"UPDATE t SET a=1 WHERE id=5\", nil)\nif err != nil && strings.Contains(err.Error(), \"VT09002\") {\n    conn.Execute(\"USE ks@primary\", nil)\n    // retry the statement\n}","preventionTips":["Reset session targets after read-only work","Use separate connections/pools for read (replica) and write (primary) traffic","Assert target type before issuing DML"],"tags":["vtgate","dml","routing","unsupported"],"backgroundTag":"dml-on-replica-target","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}