{"record":{"id":"2918e7b0f44c5736","repo":"vitessio/vitess","slug":"empty-gtid-passed-to-setposition","errorCode":null,"errorMessage":"empty gtid passed to setPosition","messagePattern":"empty gtid passed to setPosition","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vttablet/tabletserver/vstreamer/uvstreamer.go","lineNumber":570,"sourceCode":"\t\t\t\t\tLastpk:    nil,\n\t\t\t\t},\n\t\t\t\tCompleted: true,\n\t\t\t},\n\t\t},\n\t\t{Type: binlogdatapb.VEventType_COMMIT},\n\t}\n\tif err := uvs.send(evs); err != nil {\n\t\treturn err\n\t}\n\n\tdelete(uvs.plans, tableName)\n\tuvs.tablesToCopy = uvs.tablesToCopy[1:]\n\treturn nil\n}\n\nfunc (uvs *uvstreamer) setPosition(gtid string, isInTx bool) error {\n\tif gtid == \"\" {\n\t\treturn errors.New(\"empty gtid passed to setPosition\")\n\t}\n\tpos, err := replication.DecodePosition(gtid)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif pos.Equal(uvs.pos) {\n\t\treturn nil\n\t}\n\tgtidEvent := &binlogdatapb.VEvent{\n\t\tType:     binlogdatapb.VEventType_GTID,\n\t\tGtid:     gtid,\n\t\tKeyspace: uvs.vse.keyspace,\n\t\tShard:    uvs.vse.shard,\n\t}\n\n\tvar evs []*binlogdatapb.VEvent\n\tif !isInTx {\n\t\tevs = append(evs, &binlogdatapb.VEvent{","sourceCodeStart":552,"sourceCodeEnd":588,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vttablet/tabletserver/vstreamer/uvstreamer.go#L552-L588","documentation":"setPosition was invoked with an empty GTID string, which cannot be decoded into a replication position. The uvstreamer validates the GTID before calling replication.DecodePosition and rejects empty input outright. This protects the stream from resetting or corrupting its position with a meaningless value.","triggerScenarios":"The anonymous callback handling COM_BINLOG_GTID / relay-log rotate events, or sendFieldEvent, forwards a GTID string that is empty — typically when an event carries no GTID (e.g. non-GTID binlog segments) and the caller doesn't skip it.","commonSituations":"Streaming from a MySQL source with GTID mode disabled or events emitted before the first GTID; a malformed upstream event stream producing empty gtid fields.","solutions":["Enable GTID mode on the source MySQL (gtid_mode=ON) so every event carries a GTID","Skip or defer setPosition calls when the incoming gtid string is empty","Check the upstream vstreamer/filter for bugs that emit rows before the first GTID event"],"exampleFix":"// before\nif err := uvs.setPosition(gtid, isInTx); err != nil {...}\n// after\nif gtid != \"\" {\n\tif err := uvs.setPosition(gtid, isInTx); err != nil {...}\n}","handlingStrategy":"validation","validationCode":"if gtid == \"\" {\n\treturn fmt.Errorf(\"refusing to set position: empty gtid\")\n}","typeGuard":"func hasGTID(gtid string) bool { return strings.TrimSpace(gtid) != \"\" }","tryCatchPattern":"if err := uvs.setPosition(gtid, isInTx); err != nil {\n\tif strings.Contains(err.Error(), \"empty gtid\") {\n\t\t// skip event / log and continue\n\t\tcontinue\n\t}\n\treturn err\n}","preventionTips":["Run sources with gtid_mode=ON for vreplication","Skip events that carry no GTID instead of forwarding them","Monitor upstream event streams for malformed early events"],"tags":["vreplication","gtid","binlog"],"backgroundTag":"empty-gtid-position","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}