netbirdio/netbird · error · firewall.ErrIPv6NotInitialized
add route filtering: %w
Error message
add route filtering: %w
What it means
AddRouteFiltering classified the rule as IPv6 via isIPv6RouteRule - the destination is a v6 prefix, or destination is not a prefix and the first source prefix is v6 - but the manager has no v6 half. Wraps the sentinel firewall.ErrIPv6NotInitialized, meaning the v6 capability decision (HasIPv6 at Create time) does not match the traffic being programmed.
Source
Thrown at client/firewall/iptables/manager_linux.go:216
return nil, fmt.Errorf("add peer filtering for %s: %w", ip, firewall.ErrIPv6NotInitialized)
}
return m.aclMgr6.AddPeerFiltering(id, ip, proto, sPort, dPort, action, ipsetName)
}
func (m *Manager) AddRouteFiltering(
id []byte,
sources []netip.Prefix,
destination firewall.Network,
proto firewall.Protocol,
sPort, dPort *firewall.Port,
action firewall.Action,
) (firewall.Rule, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
if isIPv6RouteRule(sources, destination) {
if !m.hasIPv6() {
return nil, fmt.Errorf("add route filtering: %w", firewall.ErrIPv6NotInitialized)
}
return m.router6.AddRouteFiltering(id, sources, destination, proto, sPort, dPort, action)
}
return m.router.AddRouteFiltering(id, sources, destination, proto, sPort, dPort, action)
}
func isIPv6RouteRule(sources []netip.Prefix, destination firewall.Network) bool {
if destination.IsPrefix() {
return destination.Prefix.Addr().Is6()
}
return len(sources) > 0 && sources[0].Addr().Is6()
}
// DeletePeerRule from the firewall by rule definition
func (m *Manager) DeletePeerRule(rule firewall.Rule) error {
m.mutex.Lock()
defer m.mutex.Unlock()View on GitHub (pinned to 93e97f4bf1)
Solutions
- Assign the peer a v6 address and restart the agent so the v6 router exists
- Scope the v6 route's distribution groups in management so v4-only peers do not receive it
- In custom route managers, drop v6 route rules when !wgIface.Address().HasIPv6() instead of forwarding them to the firewall
Example fix
// before
rule, err := mgr.AddRouteFiltering(id, sources, dest, proto, sPort, dPort, action)
if err != nil {
return err // v6 route on v4-only manager fails the apply
}
// after
if isV6 := (dest.IsPrefix() && dest.Prefix.Addr().Is6()) || (!dest.IsPrefix() && len(sources) > 0 && sources[0].Addr().Is6()); isV6 && !wgIface.Address().HasIPv6() {
log.Debugf("skipping v6 route rule: no v6 overlay")
continue
}
rule, err := mgr.AddRouteFiltering(id, sources, dest, proto, sPort, dPort, action) Defensive patterns
Strategy: validation
Validate before calling
func isV6RouteRule(sources []netip.Prefix, destination firewall.Network) bool {
if destination.IsPrefix() {
return destination.Prefix.Addr().Is6()
}
return len(sources) > 0 && sources[0].Addr().Is6()
}
// before applying route rules:
if isV6RouteRule(sources, destination) && !wgIface.Address().HasIPv6() {
log.Debugf("skipping v6 route rule: no v6 firewall")
continue
} Try / catch
rule, err := mgr.AddRouteFiltering(id, sources, destination, proto, sPort, dPort, action)
if err != nil {
if errors.Is(err, firewall.ErrIPv6NotInitialized) {
continue // v6 route on a v4-only overlay: skip, keep applying the rest
}
return err
} Prevention
- Classify rule family (mirror isIPv6RouteRule) before calling the firewall
- Re-check HasIPv6() after interface address changes, not only at startup
- Scope v6 network routes in management to v6-capable peer groups
When it happens
Trigger: Calling AddRouteFiltering with destination.Prefix.Addr().Is6() true, or with the first entry of sources being a v6 prefix, while the local overlay address lacks IPv6.
Common situations: Management distributes an IPv6 network route (v6 resource range, or a v6 exit node) to a peer whose WgAddr is v4-only; network routes regenerated after the agent connected without v6.
Related errors
- reset v6 router: %w
- create v6 acl manager: %w
- add peer filtering for %s: %w
- add NAT rule: %w
- add v6 NAT rule: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/139ec901c08d17c5.
Report an issue: GitHub.