netbirdio/netbird · error
failed to add protocol address: %s
Error message
failed to add protocol address: %s
What it means
Returned by forwarder.New (client/firewall/uspfilter/forwarder/forwarder.go:96) when s.AddProtocolAddress on NIC 1 with the overlay's IPv4 address fails. gVisor rejects protocol-address additions for a non-existent NIC, a duplicate address, or an invalid prefix length; since the address comes from iface.Address().IP.As4() with Bits() from the network, a zero/invalid interface address or out-of-range prefix length produces this. The message uses %s on a tcpip error (not wrapped) and duplicates the 'failed to' wording - the underlying error type is lost to callers.
Source
Thrown at client/firewall/uspfilter/forwarder/forwarder.go:96
logger: logger,
device: iface.GetWGDevice(),
}
endpoint.mtu.Store(uint32(mtu))
if err := s.CreateNIC(nicID, endpoint); err != nil {
return nil, fmt.Errorf("create NIC: %v", err)
}
protoAddr := tcpip.ProtocolAddress{
Protocol: ipv4.ProtocolNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: tcpip.AddrFrom4(iface.Address().IP.As4()),
PrefixLen: iface.Address().Network.Bits(),
},
}
if err := s.AddProtocolAddress(nicID, protoAddr, stack.AddressProperties{}); err != nil {
return nil, fmt.Errorf("failed to add protocol address: %s", err)
}
if v6 := iface.Address().IPv6; v6.IsValid() {
v6Addr := tcpip.ProtocolAddress{
Protocol: ipv6.ProtocolNumber,
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: tcpip.AddrFrom16(v6.As16()),
PrefixLen: iface.Address().IPv6Net.Bits(),
},
}
if err := s.AddProtocolAddress(nicID, v6Addr, stack.AddressProperties{}); err != nil {
return nil, fmt.Errorf("add IPv6 protocol address: %s", err)
}
}
defaultSubnet, err := tcpip.NewSubnet(
tcpip.AddrFrom4([4]byte{0, 0, 0, 0}),
tcpip.MaskFromBytes([]byte{0, 0, 0, 0}),View on GitHub (pinned to 93e97f4bf1)
Solutions
- Require iface.Address().IP.IsValid() and 0 <= Network.Bits() <= 32 (and v6 bits <= 128) before constructing the forwarder
- Retry EnableRouting after the interface is configured; construction then receives a valid address
- When adding the optional v6 leg, keep the existing IsValid() guard so an absent v6 skips instead of failing
- Wrap the error with %w so callers can match tcpip error types
Example fix
// before
if err := s.AddProtocolAddress(nicID, protoAddr); err != nil {
return nil, fmt.Errorf("failed to add protocol address: %s", err)
}
// after - validate inputs, preserve the error chain
addr := iface.Address()
if !addr.IP.IsValid() || addr.Network.Bits() > 32 {
return nil, fmt.Errorf("invalid overlay address %s/%d", addr.IP, addr.Network.Bits())
}
if err := s.AddProtocolAddress(nicID, protoAddr); err != nil {
return nil, fmt.Errorf("add protocol address %s: %w", protoAddr.AddressWithPrefix, err)
} Defensive patterns
Strategy: validation
Validate before calling
a := iface.Address()
if !a.IP.IsValid() || !a.IP.Is4() || a.Network.Bits() > 32 {
return fmt.Errorf("forwarder needs a valid IPv4 overlay address, got %s/%d", a.IP, a.Network.Bits())
}
_ = fw.EnableRouting() Type guard
func validOverlayAddress(a common.WGAddress) bool {
return a.IP.IsValid() && a.IP.Is4() && a.Network.Bits() <= 32 &&
(!a.IPv6.IsValid() || a.IPv6Net.Bits() <= 128)
} Try / catch
if err := fw.EnableRouting(); err != nil {
msg := err.Error()
if strings.Contains(msg, "protocol address") {
// interface not addressed yet; retry once after bring-up
time.Sleep(settleDelay)
return fw.EnableRouting()
}
return err
} Prevention
- Enable routing only after the interface address is assigned and valid
- Keep the IPv6 address optional (IsValid guard) so a missing v6 never fails the v4 forwarder
- Fix the wrapper to use %w instead of %s so tcpip errors stay inspectable
When it happens
Trigger: forwarder.New called before the interface address was assigned, so Address().IP is the zero value and/or Network.Bits() is invalid; duplicate AddProtocolAddress after a double init; prefix length > 32 reaching tcpip.
Common situations: Routing enabled in a bring-up race before address configuration completes; management delivering a network address whose prefix length the agent did not sanitize; embedded/wasm paths where Address() is populated asynchronously.
Related errors
- create forwarder: %w
- create NIC: %v
- add IPv6 protocol address: %s
- creating default subnet: %w
- creating default v6 subnet: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/3907f40030597ed9.
Report an issue: GitHub.