vitessio/vitess · error
empty gtid passed to setPosition
Error message
empty gtid passed to setPosition
What it means
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.
Source
Thrown at go/vt/vttablet/tabletserver/vstreamer/uvstreamer.go:570
Lastpk: nil,
},
Completed: true,
},
},
{Type: binlogdatapb.VEventType_COMMIT},
}
if err := uvs.send(evs); err != nil {
return err
}
delete(uvs.plans, tableName)
uvs.tablesToCopy = uvs.tablesToCopy[1:]
return nil
}
func (uvs *uvstreamer) setPosition(gtid string, isInTx bool) error {
if gtid == "" {
return errors.New("empty gtid passed to setPosition")
}
pos, err := replication.DecodePosition(gtid)
if err != nil {
return err
}
if pos.Equal(uvs.pos) {
return nil
}
gtidEvent := &binlogdatapb.VEvent{
Type: binlogdatapb.VEventType_GTID,
Gtid: gtid,
Keyspace: uvs.vse.keyspace,
Shard: uvs.vse.shard,
}
var evs []*binlogdatapb.VEvent
if !isInTx {
evs = append(evs, &binlogdatapb.VEvent{View on GitHub (pinned to 01a25a7d17)
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
Example fix
// before
if err := uvs.setPosition(gtid, isInTx); err != nil {...}
// after
if gtid != "" {
if err := uvs.setPosition(gtid, isInTx); err != nil {...}
} Defensive patterns
Strategy: validation
Validate before calling
if gtid == "" {
return fmt.Errorf("refusing to set position: empty gtid")
} Type guard
func hasGTID(gtid string) bool { return strings.TrimSpace(gtid) != "" } Try / catch
if err := uvs.setPosition(gtid, isInTx); err != nil {
if strings.Contains(err.Error(), "empty gtid") {
// skip event / log and continue
continue
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- GetPreviousGTIDs: previous GTIDs not found
- partial row image encountered: ensure binlog_row_image is se
- startPos.GTIDSet is wrong type - expected filePosGTID, got:
- failed to get primary position: %v
- stream error @ (including the GTID we failed to process) %v:
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/2918e7b0f44c5736.
Report an issue: GitHub.