netbirdio/netbird · error · firewall.ErrIPv6NotInitialized
add NAT rule: %w
Error message
add NAT rule: %w
What it means
AddNatRule received a RouterPair whose destination is an IPv6 prefix, but the manager was created without the v6 half. Wraps the sentinel firewall.ErrIPv6NotInitialized; the NAT rule is refused before any iptables call is made. Note the sibling path: a v4-destination dynamic pair also mirrors into v6 when available - this error is the pure-v6 destination variant.
Source
Thrown at client/firewall/iptables/manager_linux.go:273
}
return m.router.DeleteRouteRule(rule)
}
func (m *Manager) IsServerRouteSupported() bool {
return true
}
func (m *Manager) IsStateful() bool {
return true
}
func (m *Manager) AddNatRule(pair firewall.RouterPair) error {
m.mutex.Lock()
defer m.mutex.Unlock()
if pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() {
if !m.hasIPv6() {
return fmt.Errorf("add NAT rule: %w", firewall.ErrIPv6NotInitialized)
}
return m.router6.AddNatRule(pair)
}
if err := m.router.AddNatRule(pair); err != nil {
return err
}
// Dynamic routes need NAT in both tables since resolved IPs can be
// either v4 or v6. This covers both DomainSet (modern) and the legacy
// wildcard 0.0.0.0/0 destination where the client resolves DNS.
if m.hasIPv6() && pair.Dynamic {
v6Pair := firewall.ToV6NatPair(pair)
if err := m.router6.AddNatRule(v6Pair); err != nil {
return fmt.Errorf("add v6 NAT rule: %w", err)
}
}
View on GitHub (pinned to 93e97f4bf1)
Solutions
- Enable IPv6 for the peer (v6 address in the management network) and reconnect so Create builds router6
- Exclude v4-only peers from the v6 route's groups
- Skip v6-destination pairs in the caller when !wgIface.Address().HasIPv6()
Example fix
// before
if err := mgr.AddNatRule(pair); err != nil {
return err
}
// after
if pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() && !wgIface.Address().HasIPv6() {
log.Debugf("skipping v6 NAT pair: no v6 overlay")
return nil
}
if err := mgr.AddNatRule(pair); err != nil {
return err
} Defensive patterns
Strategy: validation
Validate before calling
if pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() && !wgIface.Address().HasIPv6() {
return nil // v6 NAT pair cannot be programmed; nothing attempted yet
} Try / catch
if err := mgr.AddNatRule(pair); err != nil {
if errors.Is(err, firewall.ErrIPv6NotInitialized) {
log.Debugf("v6 NAT pair skipped: no v6 firewall")
return nil
}
return err
} Prevention
- Check the pair's destination family against HasIPv6() before AddNatRule
- Handle the sentinel explicitly with errors.Is instead of failing the whole route apply
- Keep the peer's overlay address family aligned with the routes it receives
When it happens
Trigger: Calling AddNatRule(pair) where pair.Destination.IsPrefix() && pair.Destination.Prefix.Addr().Is6() while m.ipv6Client == nil.
Common situations: A v6 network route (masquerade/NAT) applied to a peer with a v4-only overlay address; routes pushed to groups that include v4-only peers.
Related errors
- add v6 NAT rule: %w
- remove v6 NAT rule: %w
- create v6 acl manager: %w
- add peer filtering for %s: %w
- add route filtering: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/6f50da3f6f92999e.
Report an issue: GitHub.