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

  1. Validate keys once at the boundary with wgtypes.ParseKey and pass key.String() onward
  2. If the key is hex, convert it first: bytes, err := hex.DecodeString(hexKey) then wgtypes.NewKey(bytes)
  3. 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

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


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/bb79007c1df343b8. Report an issue: GitHub.