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

  1. Enable GTID mode on the source MySQL (gtid_mode=ON) so every event carries a GTID
  2. Skip or defer setPosition calls when the incoming gtid string is empty
  3. 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

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


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