netbirdio/netbird · warning
decode peerID: %w
Error message
decode peerID: %w
What it means
parseTransfers, which turns a wireguard-go IpcGet dump into per-peer stats, failed to hex-decode the value of a public_key= line with hex.DecodeString. The wireguard-go UAPI always emits public keys as 64 lowercase hex characters, so this only fires when the dump is malformed, comes from an incompatible producer, or was corrupted. The decoded bytes are re-encoded to base64 to key the stats map.
Source
Thrown at client/iface/configurer/usp.go:384
func parseTransfers(ipc string) (map[string]WGStats, error) {
stats := make(map[string]WGStats)
var (
currentKey string
currentStats WGStats
hasPeer bool
)
lines := strings.Split(ipc, "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
// If we're within the details of the found peer and encounter another public key,
// this means we're starting another peer's details. So, stop.
if strings.HasPrefix(line, "public_key=") {
peerID := strings.TrimPrefix(line, "public_key=")
h, err := hex.DecodeString(peerID)
if err != nil {
return nil, fmt.Errorf("decode peerID: %w", err)
}
currentKey = base64.StdEncoding.EncodeToString(h)
currentStats = WGStats{} // Reset stats for the new peer
hasPeer = true
stats[currentKey] = currentStats
continue
}
if !hasPeer {
continue
}
key := strings.SplitN(line, "=", 2)
if len(key) != 2 {
continue
}
switch key[0] {
case ipcKeyLastHandshakeTimeSec:View on GitHub (pinned to 93e97f4bf1)
Solutions
- Validate the producer: only pass IpcGet output from the matching wireguard-go device
- Skip unparseable public_key lines (log at debug) rather than failing the whole stats fetch
- Pin the wireguard-go version and add a fixture test for the dump format
- Check the raw dump for truncation when this appears in logs
Example fix
// before
h, err := hex.DecodeString(peerID)
if err != nil {
return nil, fmt.Errorf("decode peerID: %w", err)
}
// after: skip malformed keys instead of dropping all stats
h, err := hex.DecodeString(peerID)
if err != nil {
log.Debugf("skipping malformed public_key line: %v", err)
continue
} Defensive patterns
Strategy: type-guard
Validate before calling
// sanity-check a UAPI public_key line before relying on it
func validPublicKeyLine(line string) bool {
v := strings.TrimPrefix(line, "public_key=")
return len(v) == 64
} Type guard
func isHexPeerID(s string) bool {
if len(s) != 64 {
return false
}
_, err := hex.DecodeString(s)
return err == nil
} Try / catch
stats, err := uspCfg.GetStats()
if err != nil {
if strings.Contains(err.Error(), "decode peerID") {
// malformed/foreign IPC dump: log and return partial stats rather than failing
log.Warnf("stats parse hit malformed peer key: %v", err)
return map[string]configurer.WGStats{}, nil
}
return nil, err
} Prevention
- Only feed IpcGet output from the paired wireguard-go version into stats parsing
- Skip malformed public_key lines instead of failing the entire stats fetch
- Pin the wireguard-go dependency and fixture-test its UAPI format
- Log dump length and offending line when hex decoding fails
When it happens
Trigger: An IPC string not produced by the paired wireguard-go device (hand-crafted test input, foreign implementation); truncated or whitespace-padded public_key line; memory corruption of the dumped string.
Common situations: Unit tests feeding synthetic IPC strings with wrong key formats; a vendored wireguard-go fork emitting base64 keys; version skew after upgrading the wireguard-go dependency.
Related errors
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/f2eab06a472c432f.
Report an issue: GitHub.