netbirdio/netbird · error
parse peer key: %w
Error message
parse peer key: %w
What it means
RemoveAllowedIP could not parse the peer public key with wgtypes.ParseKey. wgtypes.ParseKey accepts only a base64-encoded 32-byte key (44 characters ending in '='), the format wgtypes.Key.String() produces. Hex-encoded keys, truncated strings, whitespace, or empty input all fail here before any device interaction happens.
Source
Thrown at client/iface/configurer/kernel_unix.go:183
config := wgtypes.Config{
Peers: []wgtypes.PeerConfig{peer},
}
err = c.configure(config)
if err != nil {
return fmt.Errorf(`received error "%w" while adding allowed Ip to peer on interface %s with settings: allowed ips %s`, err, c.deviceName, allowedIP)
}
return nil
}
func (c *KernelConfigurer) RemoveAllowedIP(peerKey string, allowedIP netip.Prefix) error {
ipNet := net.IPNet{
IP: allowedIP.Addr().AsSlice(),
Mask: net.CIDRMask(allowedIP.Bits(), allowedIP.Addr().BitLen()),
}
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return fmt.Errorf("parse peer key: %w", err)
}
existingPeer, err := c.getPeer(c.deviceName, peerKey)
if err != nil {
return fmt.Errorf("get peer: %w", err)
}
newAllowedIPs := existingPeer.AllowedIPs
for i, existingAllowedIP := range existingPeer.AllowedIPs {
if existingAllowedIP.String() == ipNet.String() {
newAllowedIPs = append(existingPeer.AllowedIPs[:i], existingPeer.AllowedIPs[i+1:]...) //nolint:gocritic
break
}
}
peer := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,View on GitHub (pinned to 93e97f4bf1)
Solutions
- Validate keys once at the boundary with wgtypes.ParseKey and pass key.String() onward
- If the key is hex, convert it first: bytes, err := hex.DecodeString(hexKey) then wgtypes.NewKey(bytes)
- Check the upstream producer (network map handling) for truncation or formatting of peer keys
Example fix
// before: hex key from a UAPI dump fails base64 parsing
err := cfg.RemoveAllowedIP(hexKey, prefix)
// after: convert hex to a wgtypes key once at the boundary
b, err := hex.DecodeString(hexKey)
if err != nil {
return err
}
key, err := wgtypes.NewKey(b)
if err != nil {
return err
}
err = cfg.RemoveAllowedIP(key.String(), prefix) Defensive patterns
Strategy: validation
Validate before calling
// validate/normalize once at the boundary before any configurer call
parsedKey, err := wgtypes.ParseKey(peerKey)
if err != nil {
return fmt.Errorf("reject malformed peer key: %w", err)
}
peerKey = parsedKey.String() Type guard
func isValidWGPeerKey(s string) bool {
_, err := wgtypes.ParseKey(s)
return err == nil
} Try / catch
if err := cfg.RemoveAllowedIP(peerKey, prefix); err != nil {
if strings.Contains(err.Error(), "parse peer key") {
// key format bug upstream: fix the producer, do not retry
}
return err
} Prevention
- Parse peer keys once where they enter the system and pass key.String() everywhere after
- Convert hex UAPI keys to wgtypes.Key with hex.DecodeString + wgtypes.NewKey, never mix formats
- Trim whitespace on keys coming from config files or IPC payloads
- Add a unit test asserting wgtypes.ParseKey accepts every key your pipeline produces
When it happens
Trigger: Passing the hex form of the key (64 hex chars, as used in wireguard-go UAPI public_key= lines) where base64 is required; peer key truncated or with trailing whitespace/newline; empty peerKey string; key sourced from a different serialization format.
Common situations: Copying keys between the kernel configurer (base64) and userspace UAPI dumps (hex) without converting; management payload carrying a malformed key; test fixtures using placeholder strings like 'testkey'.
Related errors
- parse peer key: %w
- connector type change not allowed
- service name is required
- service name exceeds maximum length of 255 characters
- at least one target is required
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/bb79007c1df343b8.
Report an issue: GitHub.