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
- Guard with endpoint != nil && endpoint.IP != nil before parsing
- Build the value with netip.AddrFromSlice(endpoint.IP) and skip on !ok instead of round-tripping through String()
- Fix the upstream producer to always set IP and Port together on the UDPAddr
- 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
- Never build net.UDPAddr without setting both IP and Port
- Convert addresses with netip.AddrFromSlice instead of String()/ParseAddr round-trips
- Validate endpoints at the boundary where ICE/relay results are converted
- Remember IpcSet already succeeded when this error fires; do not blindly retry the whole update
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- error configuring interface: %s
- error configuring interface: %s
- no keys found in bundle
- failed to decode PEM data
- failed to parse private root key: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/c8addc8a0556ceb8.
Report an issue: GitHub.