vitessio/vitess · critical

cannot decode RelayLogFilePosition

Error message

cannot decode RelayLogFilePosition

What it means

Same panic family as error 1630 but for the RelayLogFilePosition field: ProtoToReplicationStatus calls DecodePosition on s.RelayLogFilePosition and panics if it cannot parse the 'file:pos' string. The panic name explicitly identifies the field so the bad value can be located. It guards against invalid relay-log position data entering ReplicationStatus.

Source

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

	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"))
		}
	}
	replstatus := ReplicationStatus{
		Position:                               pos,
		RelayLogPosition:                       relayPos,
		FilePosition:                           filePos,
		RelayLogSourceBinlogEquivalentPosition: fileRelayPos,
		RelayLogFilePosition:                   relayFilePos,
		SourceServerID:                         s.SourceServerId,
		ReplicationLagSeconds:                  s.ReplicationLagSeconds,
		ReplicationLagUnknown:                  s.ReplicationLagUnknown,
		SQLDelay:                               s.SqlDelay,

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Inspect s.RelayLogFilePosition and correct it to 'file:position' format.
  2. Pre-validate with mysql.DecodePosition before conversion.
  3. Regenerate the status from the tablet via a fresh status RPC instead of reusing stale/mutated protos.

Example fix

// before
status := mysql.ProtoToReplicationStatus(pb) // panics on bad RelayLogFilePosition
// after
if _, err := mysql.DecodePosition(pb.RelayLogFilePosition); err != nil {
    return fmt.Errorf("bad RelayLogFilePosition %q: %w", pb.RelayLogFilePosition, err)
}
status := mysql.ProtoToReplicationStatus(pb)
Defensive patterns

Strategy: validation

Validate before calling

if _, err := mysql.DecodePosition(pb.RelayLogFilePosition); err != nil {
    return fmt.Errorf("invalid RelayLogFilePosition %q", pb.RelayLogFilePosition)
}

Try / catch

defer func() { if r := recover(); r != nil { err = fmt.Errorf("conversion failed: %v", r) } }()

Prevention

When it happens

Trigger: ProtoToReplicationStatus (or callers findErrantGTIDs, FindPositionsOfAllCandidates, ReplicaWasRunning, replicaIOThreadWasRunning) receives a proto where RelayLogFilePosition is malformed — e.g. missing ':offset' suffix, non-numeric offset, or partially written data.

Common situations: Hand-built protos in tests/tooling, data corruption from external serialization round-trips, or copy-paste errors putting a GTID or UUID into the position field.

Related errors


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