vitessio/vitess · critical
cannot decode RelayLogSourceBinlogEquivalentPosition
Error message
cannot decode RelayLogSourceBinlogEquivalentPosition
What it means
ProtoToReplicationStatus converts a protobuf ReplicationStatus into the in-memory ReplicationStatus struct. Each position field is decoded with DecodePosition, and any decode failure triggers an immediate panic wrapped with the offending field name. This panic means the RelayLogSourceBinlogEquivalentPosition string in the proto is not a valid binlog position (expected 'file:pos' format). It indicates corrupted or hand-mangled status data, since protos are normally produced by ReplicationStatusToProto.
Source
Thrown at go/mysql/replication/replication_status.go:149
}
// 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"))
}
}
replstatus := ReplicationStatus{
Position: pos,
RelayLogPosition: relayPos,
FilePosition: filePos,
RelayLogSourceBinlogEquivalentPosition: fileRelayPos,
RelayLogFilePosition: relayFilePos,View on GitHub (pinned to 01a25a7d17)
Solutions
- Log/inspect s.RelayLogSourceBinlogEquivalentPosition and fix the producer to write valid 'file:position' strings via ReplicationStatusToProto.
- Validate the position string with mysql.DecodePosition before calling ProtoToReplicationStatus.
- If the field is legitimately unavailable, leave it as empty string only if the consumer treats empty as zero position — otherwise populate it correctly.
Example fix
// before
st := &replicationdatapb.ReplicationStatus{RelayLogSourceBinlogEquivalentPosition: "mysql-bin.000001"}
status := mysql.ProtoToReplicationStatus(st) // panics
// after
st := &replicationdatapb.ReplicationStatus{RelayLogSourceBinlogEquivalentPosition: "mysql-bin.000001:1234"}
if _, err := mysql.DecodePosition(st.RelayLogSourceBinlogEquivalentPosition); err == nil {
status := mysql.ProtoToReplicationStatus(st)
} Defensive patterns
Strategy: validation
Validate before calling
if _, err := mysql.DecodePosition(pb.RelayLogSourceBinlogEquivalentPosition); err != nil {
return fmt.Errorf("invalid RelayLogSourceBinlogEquivalentPosition %q", pb.RelayLogSourceBinlogEquivalentPosition)
} Try / catch
// Go has no try/catch; isolate the panic:
func safeConvert(pb *replicationdatapb.ReplicationStatus) (st *mysql.ReplicationStatus, err error) {
defer func() { if r := recover(); r != nil { err = fmt.Errorf("ProtoToReplicationStatus panicked: %v", r) } }()
s := mysql.ProtoToReplicationStatus(pb)
return &s, nil
} Prevention
- Always produce status protos via ReplicationStatusToProto, never hand-built literals.
- Pre-validate all position fields with DecodePosition before conversion.
- Round-trip test: ReplicationStatusToProto(ProtoToReplicationStatus(x)) in CI.
When it happens
Trigger: Calling ProtoToReplicationStatus (directly or via findErrantGTIDs, FindPositionsOfAllCandidates, ReplicaWasRunning, replicaIOThreadWasRunning) with a proto whose RelayLogSourceBinlogEquivalentPosition is empty-but-nonzero-usage, truncated, or not in 'file:position' format (e.g. 'mysql-bin.000001' without ':123', or negative/garbage position).
Common situations: Manually constructed protobufs in tests or tools, status data round-tripped through external systems that altered the string, or legacy/custom control code writing fields in the wrong format.
Related errors
- cannot decode RelayLogFilePosition
- cannot decode SourceUUID
- The receiver ReplicationStatus contained a Mysql56GTIDSet in
- Last_SQL_Error: ${LastSQL_Error}, Last_IO_Error: ${LastIO_Er
- GetPreviousGTIDs: previous GTIDs not found
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/b2da7b23bab76934.
Report an issue: GitHub.