netbirdio/netbird · error
remove inbound DNAT: %w
Error message
remove inbound DNAT: %w
What it means
RemoveInboundDNAT for a v6 local address on a manager without the v6 half. It wraps firewall.ErrIPv6NotInitialized like the add path; note that in the current code this fires only when hasIPv6() is false - if the manager has v6, removal is delegated to router6 and cannot produce this sentinel.
Source
Thrown at client/firewall/iptables/manager_linux.go:486
defer m.mutex.Unlock()
if localAddr.Is6() {
if !m.hasIPv6() {
return fmt.Errorf("add inbound DNAT: %w", firewall.ErrIPv6NotInitialized)
}
return m.router6.AddInboundDNAT(localAddr, protocol, originalPort, translatedPort)
}
return m.router.AddInboundDNAT(localAddr, protocol, originalPort, translatedPort)
}
// RemoveInboundDNAT removes an inbound DNAT rule.
func (m *Manager) RemoveInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, originalPort, translatedPort uint16) error {
m.mutex.Lock()
defer m.mutex.Unlock()
if localAddr.Is6() {
if !m.hasIPv6() {
return fmt.Errorf("remove inbound DNAT: %w", firewall.ErrIPv6NotInitialized)
}
return m.router6.RemoveInboundDNAT(localAddr, protocol, originalPort, translatedPort)
}
return m.router.RemoveInboundDNAT(localAddr, protocol, originalPort, translatedPort)
}
// AddOutputDNAT adds an OUTPUT chain DNAT rule for locally-generated traffic.
func (m *Manager) AddOutputDNAT(localAddr netip.Addr, protocol firewall.Protocol, originalPort, translatedPort uint16) error {
m.mutex.Lock()
defer m.mutex.Unlock()
if localAddr.Is6() {
if !m.hasIPv6() {
return fmt.Errorf("add output DNAT: %w", firewall.ErrIPv6NotInitialized)
}
return m.router6.AddOutputDNAT(localAddr, protocol, originalPort, translatedPort)
}
return m.router.AddOutputDNAT(localAddr, protocol, originalPort, translatedPort)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Treat as a skip in callers: the rule cannot exist on this manager, so nothing to remove
- Match errors.Is(err, firewall.ErrIPv6NotInitialized) and continue teardown of the remaining rules
- Restore v6 on the peer if v6 inbound DNAT must keep working
Example fix
// before
if err := mgr.RemoveInboundDNAT(localAddr, proto, origPort, xlatePort); err != nil {
return err // aborts teardown of remaining rules
}
// after
if err := mgr.RemoveInboundDNAT(localAddr, proto, origPort, xlatePort); err != nil {
if errors.Is(err, firewall.ErrIPv6NotInitialized) {
log.Debugf("no v6 manager; v6 inbound DNAT rule cannot exist")
} else {
return err
}
} Defensive patterns
Strategy: validation
Validate before calling
if localAddr.Is6() && !wgIface.Address().HasIPv6() {
return nil // the rule cannot exist on this manager; nothing to remove
} Try / catch
if err := mgr.RemoveInboundDNAT(localAddr, proto, origPort, xlatePort); err != nil {
if errors.Is(err, firewall.ErrIPv6NotInitialized) {
log.Debugf("v6 inbound DNAT rule cannot exist without v6 manager")
return nil // continue teardown of remaining rules
}
return err
} Prevention
- During teardown, treat ErrIPv6NotInitialized as 'rule cannot exist' and skip
- Store the overlay family alongside installed rules so removals stay consistent
- Use errors.Is so the sentinel check survives message changes
When it happens
Trigger: Calling RemoveInboundDNAT with localAddr.Is6() true while m.ipv6Client == nil - e.g. tearing down a v6 inbound rule after the peer restarted without v6.
Common situations: Service teardown after the agent reconnected v4-only; configuration drift where stored rules reference v6 addresses the current overlay cannot manage.
Related errors
- remove v6 NAT rule: %w
- reset v6 acl manager: %w
- reset v6 router: %w
- add DNAT rule: %w
- add inbound DNAT: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/a310001076c9a617.
Report an issue: GitHub.