{"record":{"id":"b2da7b23bab76934","repo":"vitessio/vitess","slug":"cannot-decode-relaylogsourcebinlogequivalentpositi","errorCode":null,"errorMessage":"cannot decode RelayLogSourceBinlogEquivalentPosition","messagePattern":"cannot decode RelayLogSourceBinlogEquivalentPosition","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"go/mysql/replication/replication_status.go","lineNumber":149,"sourceCode":"}\n\n// ProtoToReplicationStatus translates a proto Status, or panics.\nfunc ProtoToReplicationStatus(s *replicationdatapb.Status) ReplicationStatus {\n\tpos, err := DecodePosition(s.Position)\n\tif err != nil {\n\t\tpanic(vterrors.Wrapf(err, \"cannot decode Position\"))\n\t}\n\trelayPos, err := DecodePosition(s.RelayLogPosition)\n\tif err != nil {\n\t\tpanic(vterrors.Wrapf(err, \"cannot decode RelayLogPosition\"))\n\t}\n\tfilePos, err := DecodePosition(s.FilePosition)\n\tif err != nil {\n\t\tpanic(vterrors.Wrapf(err, \"cannot decode FilePosition\"))\n\t}\n\tfileRelayPos, err := DecodePosition(s.RelayLogSourceBinlogEquivalentPosition)\n\tif err != nil {\n\t\tpanic(vterrors.Wrapf(err, \"cannot decode RelayLogSourceBinlogEquivalentPosition\"))\n\t}\n\trelayFilePos, err := DecodePosition(s.RelayLogFilePosition)\n\tif err != nil {\n\t\tpanic(vterrors.Wrapf(err, \"cannot decode RelayLogFilePosition\"))\n\t}\n\tvar sid SID\n\tif s.SourceUuid != \"\" {\n\t\tsid, err = ParseSID(s.SourceUuid)\n\t\tif err != nil {\n\t\t\tpanic(vterrors.Wrapf(err, \"cannot decode SourceUUID\"))\n\t\t}\n\t}\n\treplstatus := ReplicationStatus{\n\t\tPosition:                               pos,\n\t\tRelayLogPosition:                       relayPos,\n\t\tFilePosition:                           filePos,\n\t\tRelayLogSourceBinlogEquivalentPosition: fileRelayPos,\n\t\tRelayLogFilePosition:                   relayFilePos,","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/mysql/replication/replication_status.go#L131-L167","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nst := &replicationdatapb.ReplicationStatus{RelayLogSourceBinlogEquivalentPosition: \"mysql-bin.000001\"}\nstatus := mysql.ProtoToReplicationStatus(st) // panics\n// after\nst := &replicationdatapb.ReplicationStatus{RelayLogSourceBinlogEquivalentPosition: \"mysql-bin.000001:1234\"}\nif _, err := mysql.DecodePosition(st.RelayLogSourceBinlogEquivalentPosition); err == nil {\n    status := mysql.ProtoToReplicationStatus(st)\n}","handlingStrategy":"validation","validationCode":"if _, err := mysql.DecodePosition(pb.RelayLogSourceBinlogEquivalentPosition); err != nil {\n    return fmt.Errorf(\"invalid RelayLogSourceBinlogEquivalentPosition %q\", pb.RelayLogSourceBinlogEquivalentPosition)\n}","typeGuard":null,"tryCatchPattern":"// Go has no try/catch; isolate the panic:\nfunc safeConvert(pb *replicationdatapb.ReplicationStatus) (st *mysql.ReplicationStatus, err error) {\n    defer func() { if r := recover(); r != nil { err = fmt.Errorf(\"ProtoToReplicationStatus panicked: %v\", r) } }()\n    s := mysql.ProtoToReplicationStatus(pb)\n    return &s, nil\n}","preventionTips":["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."],"tags":["mysql","replication","protobuf-decoding","panic"],"backgroundTag":"invalid-binlog-position-format","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}