vitessio/vitess · error
cannot decode Position
Error message
cannot decode Position
What it means
ProtoToReplicationStatus converts a replicationdatapb.Status proto into a ReplicationStatus struct, decoding the Position field via DecodePosition. Because protos arrive over RPC from other Vitess components, it assumes they are valid and panics (wrapped with vterrors) if the Position string cannot be decoded. This indicates a corrupt or foreign-produced proto message.
Source
Thrown at go/mysql/replication/replication_status.go:137
SqlState: int32(s.SQLState),
LastSqlError: s.LastSQLError,
SslAllowed: s.SSLAllowed,
HasReplicationFilters: s.HasReplicationFilters,
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 SIDView on GitHub (pinned to 01a25a7d17)
Solutions
- Check that the sending tablet's Status.Position is populated and valid before/after RPC (log s.Position when it fails)
- Guard the conversion by validating the position string, or use the non-panicking DecodePosition path in new code
- Look for version skew or a zero-value Status proto being passed in; fix the producer to send a real position
Example fix
// before
status := replication.ProtoToReplicationStatus(s)
// after
if _, err := replication.DecodePosition(s.Position); err != nil {
return vterrors.Wrapf(err, "tablet returned invalid Position %q", s.Position)
}
status := replication.ProtoToReplicationStatus(s) Defensive patterns
Strategy: validation
Validate before calling
if s == nil || s.Position == "" {
return errors.New("Status proto has empty Position")
}
if _, err := replication.DecodePosition(s.Position); err != nil {
return vterrors.Wrapf(err, "invalid Position %q", s.Position)
} Try / catch
func safeProtoToStatus(s *replicationdatapb.Status) (st replication.ReplicationStatus, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("ProtoToReplicationStatus panicked: %v", r)
}
}()
return replication.ProtoToReplicationStatus(s), nil
} Prevention
- Log the raw proto fields when conversion panics to identify the producing tablet
- Validate all position fields after RPC before conversion
- Check for version skew between components writing Status protos
When it happens
Trigger: Calling ProtoToReplicationStatus with a Status whose Position field is empty, truncated, or not a valid GTID set string — e.g. a zero-valued proto, or data written by a component using a different flavor encoding.
Common situations: failover/orchestrator code (findErrantGTIDs, FindPositionsOfAllCandidates, ReplicaWasRunning) receiving Status protos from tablets whose Position was never initialized or read from a misconfigured source; version-skew where a peer writes positions differently.
Related errors
- cannot decode RelayLogPosition
- cannot decode FilePosition
- err
- cannot decode RelayLogSourceBinlogEquivalentPosition
- cannot decode RelayLogFilePosition
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/323dd5e730b976ce.
Report an issue: GitHub.