netbirdio/netbird · error
add IPv6 protocol address: %s
Error message
add IPv6 protocol address: %s
What it means
Returned by forwarder.New when the gVisor netstack rejects the IPv6 address assignment for the NIC (stack.AddProtocolAddress). gVisor returns tcpip.ErrDuplicateLocalAddress when the address was already registered on that NIC, or an invalid-prefix error when PrefixLen exceeds the protocol's bit width. At this point in the constructor the IPv4 address has already been added and the NIC created, so the whole userspace forwarder fails to come up.
Source
Thrown at client/firewall/uspfilter/forwarder/forwarder.go:108
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}),
)
if err != nil {
return nil, fmt.Errorf("creating default subnet: %w", err)
}
defaultSubnetV6, err := tcpip.NewSubnet(
tcpip.AddrFrom16([16]byte{}),
tcpip.MaskFromBytes(make([]byte, 16)),
)
if err != nil {
return nil, fmt.Errorf("creating default v6 subnet: %w", err)
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Check the IPv6 address and prefix reported by iface.Address() before constructing the Forwarder: IPv6.IsValid() must be true and IPv6Net.Bits() must be 0..128
- Ensure only one Forwarder instance per stack/NIC lifecycle; tear down the old forwarder (cancel + stack cleanup) before creating a new one
- If the overlay was configured without IPv6, verify the address mapper is not returning a stale v6 address after a reconfiguration
- Upgrade gvisor.dev/gvisor if the wrapped error text points at an AddressProperties validation added in a newer version
Example fix
// before
if v6 := iface.Address().IPv6; v6.IsValid() {
// ... AddProtocolAddress
}
// after
if v6 := iface.Address().IPv6; v6.IsValid() {
if bits := iface.Address().IPv6Net.Bits(); bits < 0 || bits > 128 {
return nil, fmt.Errorf("invalid IPv6 prefix length %d", bits)
}
// ... AddProtocolAddress
} Defensive patterns
Strategy: validation
Validate before calling
// before constructing the forwarder
a := iface.Address()
if a.IPv6.IsValid() {
if bits := a.IPv6Net.Bits(); bits < 0 || bits > 128 {
return fmt.Errorf("invalid IPv6 prefix %s", a.IPv6Net)
}
}
f, err := forwarder.New(iface, logger, flowLogger, netstack, mtu) Type guard
func validV6Assignment(a wgaddr.Address) bool {
v6 := a.IPv6
if !v6.IsValid() {
return true // v4-only is fine; the v6 block is skipped
}
bits := a.IPv6Net.Bits()
return bits >= 0 && bits <= 128 && v6.Is6()
} Try / catch
if _, err := forwarder.New(...); err != nil {
if strings.Contains(err.Error(), "add IPv6 protocol address") {
// re-check address state, recreate interface, retry once
}
return err
} Prevention
- Validate interface address/prefix pairs before every forwarder construction
- Tear down and join the previous forwarder before creating a new one
- Log iface.Address() contents at debug when bring-up fails to capture the exact v6 values
When it happens
Trigger: Calling forwarder.New with an interface whose Address().IPv6 is valid but whose IPv6Net prefix bits are invalid (>128 or negative from a misparsed CIDR), or calling New twice against state where the same v6 address already exists; also a teardown race where the NIC was removed between CreateNIC and this call.
Common situations: IPv6 overlay enabled in NetBird while the interface address/prefix were populated from a malformed config or a management network range that is not a real /prefix; concurrent interface re-creation (BindUpdate/engine restart) racing the forwarder constructor; gVisor version change tightening AddProtocolAddress validation.
Related errors
- creating default v6 subnet: %w
- set promiscuous mode: %s
- set spoofing: %s
- create IPv6 firewall: %w
- create v6 router: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/2e20423e23be5d43.
Report an issue: GitHub.