netbirdio/netbird · error

failed to parse endpoint address: %w

Error message

failed to parse endpoint address: %w

What it means

WGUSPConfigurer.UpdatePeer applied the peer config to the userspace device, then failed to parse the endpoint address for the activity recorder via netip.ParseAddr(endpoint.IP.String()). When endpoint is non-nil but endpoint.IP is nil, IP.String() returns the literal '<nil>', which netip refuses to parse. Note the WireGuard config itself was already applied via IpcSet, so the error fires after the state change succeeded.

Source

Thrown at client/iface/configurer/usp.go:124

		// don't replace allowed ips, wg will handle duplicated peer IP
		AllowedIPs:                  prefixesToIPNets(allowedIps),
		PersistentKeepaliveInterval: &keepAlive,
		PresharedKey:                preSharedKey,
		Endpoint:                    endpoint,
	}

	config := wgtypes.Config{
		Peers: []wgtypes.PeerConfig{peer},
	}

	if ipcErr := c.device.IpcSet(toWgUserspaceString(config)); ipcErr != nil {
		return ipcErr
	}

	if endpoint != nil {
		addr, err := netip.ParseAddr(endpoint.IP.String())
		if err != nil {
			return fmt.Errorf("failed to parse endpoint address: %w", err)
		}
		addrPort := netip.AddrPortFrom(addr.Unmap(), uint16(endpoint.Port))
		c.activityRecorder.UpsertAddress(peerKey, addrPort)
	}
	return nil
}

func (c *WGUSPConfigurer) RemoveEndpointAddress(peerKey string) error {
	peerKeyParsed, err := wgtypes.ParseKey(peerKey)
	if err != nil {
		return fmt.Errorf("parse peer key: %w", err)
	}

	ipcStr, err := c.device.IpcGet()
	if err != nil {
		return fmt.Errorf("get IPC config: %w", err)
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Guard with endpoint != nil && endpoint.IP != nil before parsing
  2. Build the value with netip.AddrFromSlice(endpoint.IP) and skip on !ok instead of round-tripping through String()
  3. Fix the upstream producer to always set IP and Port together on the UDPAddr
  4. Treat this as recorder-only bookkeeping: log and continue instead of failing UpdatePeer after a successful IpcSet

Example fix

// before
addr, err := netip.ParseAddr(endpoint.IP.String())
if err != nil {
	return fmt.Errorf("failed to parse endpoint address: %w", err)
}

// after
if addr, ok := netip.AddrFromSlice(endpoint.IP); ok {
	addrPort := netip.AddrPortFrom(addr.Unmap(), uint16(endpoint.Port))
	c.activityRecorder.UpsertAddress(peerKey, addrPort)
} else {
	log.Warnf("skipping activity record for peer %s: invalid endpoint IP", peerKey)
}
Defensive patterns

Strategy: validation

Validate before calling

// validate the endpoint before UpdatePeer reaches the recorder step
func validEndpoint(ep *net.UDPAddr) bool {
	return ep != nil && ep.IP != nil && ep.Port > 0 && ep.Port <= 65535
}

if !validEndpoint(endpoint) {
	endpoint = nil // UpdatePeer accepts a nil endpoint
}

Type guard

func hasParsableEndpoint(ep *net.UDPAddr) bool {
	if ep == nil || ep.IP == nil {
		return false
	}
	_, ok := netip.AddrFromSlice(ep.IP)
	return ok
}

Try / catch

if err := uspCfg.UpdatePeer(key, prefixes, keepalive, endpoint, psk); err != nil {
	if strings.Contains(err.Error(), "failed to parse endpoint address") {
		// config already applied; only the activity recorder failed: retry with nil endpoint or log
		log.Warnf("peer applied but endpoint unparsable: %v", err)
		return nil
	}
	return err
}

Prevention

When it happens

Trigger: endpoint *net.UDPAddr constructed with a nil IP (zero-value struct or port-only address); an endpoint.IP of abnormal length that netip.ParseAddr rejects; hostname resolution upstream producing an address-less UDPAddr.

Common situations: Connection manager passing a UDPAddr built from a config field that never got filled; relay/ICE code creating UDPAddr{Port: n} before the address is known; marshaling round-trips losing the IP.

Understand the failure class

Related errors


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