vitessio/vitess · error

cannot decode RelayLogPosition

Error message

cannot decode RelayLogPosition

What it means

Same family as the Position panic: ProtoToReplicationStatus also decodes the RelayLogPosition field with DecodePosition and panics with a vterrors-wrapped error if it is invalid. The relay log position string in the incoming Status proto is empty or malformed.

Source

Thrown at go/mysql/replication/replication_status.go:141

		AutoPosition:                           s.AutoPosition,
		UsingGtid:                              s.UsingGTID,
		SemiSyncPrimaryEnabled:                 s.SemiSyncPrimaryEnabled,
		SemiSyncReplicaEnabled:                 s.SemiSyncReplicaEnabled,
		SemiSyncPrimaryStatus:                  s.SemiSyncPrimaryStatus,
		SemiSyncReplicaStatus:                  s.SemiSyncReplicaStatus,
	}
	return replstatuspb
}

// ProtoToReplicationStatus translates a proto Status, or panics.
func ProtoToReplicationStatus(s *replicationdatapb.Status) ReplicationStatus {
	pos, err := DecodePosition(s.Position)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode Position"))
	}
	relayPos, err := DecodePosition(s.RelayLogPosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode RelayLogPosition"))
	}
	filePos, err := DecodePosition(s.FilePosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode FilePosition"))
	}
	fileRelayPos, err := DecodePosition(s.RelayLogSourceBinlogEquivalentPosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode RelayLogSourceBinlogEquivalentPosition"))
	}
	relayFilePos, err := DecodePosition(s.RelayLogFilePosition)
	if err != nil {
		panic(vterrors.Wrapf(err, "cannot decode RelayLogFilePosition"))
	}
	var sid SID
	if s.SourceUuid != "" {
		sid, err = ParseSID(s.SourceUuid)
		if err != nil {
			panic(vterrors.Wrapf(err, "cannot decode SourceUUID"))

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Validate/decode s.RelayLogPosition before calling ProtoToReplicationStatus and treat empty as a valid "no relay log" state if appropriate
  2. Fix the producer (tablet) so it emits a valid or empty RelayLogPosition
  3. Check for version skew between components writing the status proto

Example fix

// before
status := replication.ProtoToReplicationStatus(s)
// after
if _, err := replication.DecodePosition(s.RelayLogPosition); err != nil {
	return vterrors.Wrapf(err, "invalid RelayLogPosition %q", s.RelayLogPosition)
}
status := replication.ProtoToReplicationStatus(s)
Defensive patterns

Strategy: validation

Validate before calling

if _, err := replication.DecodePosition(s.RelayLogPosition); err != nil {
	return vterrors.Wrapf(err, "invalid RelayLogPosition %q", s.RelayLogPosition)
}

Try / catch

defer func() {
	if r := recover(); r != nil {
		log.Warn("RelayLogPosition decode panic", slog.Any("panic", r), slog.String("raw", s.RelayLogPosition))
	}
}()

Prevention

When it happens

Trigger: Calling ProtoToReplicationStatus on a Status proto whose RelayLogPosition is empty/corrupt — common when a tablet has no relay log yet but the field was left as an invalid non-empty string, or a zero proto is converted.

Common situations: Replica status gathering during reparenting where the replica never connected to a primary (no relay log), or peers running mismatched Vitess versions.

Related errors


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