netbirdio/netbird · error

update local IPs: %w

Error message

update local IPs: %w

What it means

Returned by the uspfilter Manager constructor (client/firewall/uspfilter/filter.go:323) when m.localipmanager.UpdateLocalIPs(iface) fails. UpdateLocalIPs (localip.go:63) enumerates net.Interfaces() (whose error is only logged) and wraps the whole body in a panic-recover that converts any panic into 'panic: %v' - so this wrapper in practice fires when iface.Address() or the interface walk panics (nil IFaceMapper, unset WGIface address), not on ordinary OS errors.

Source

Thrown at client/firewall/uspfilter/filter.go:323

		routeRulesMap:       make(map[nbid.RuleID]*RouteRule),
		dnatMappings:        make(map[netip.Addr]netip.Addr),
		portDNATRules:       []portDNATRule{},
		netstackServices:    make(map[serviceKey]struct{}),
		mtu:                 mtu,
	}
	m.routingEnabled.Store(false)

	if !disableMSSClamping {
		m.mssClampEnabled = true
		if mtu > ipv4TCPHeaderMinSize {
			m.mssClampValueIPv4 = mtu - ipv4TCPHeaderMinSize
		}
		if mtu > ipv6TCPHeaderMinSize {
			m.mssClampValueIPv6 = mtu - ipv6TCPHeaderMinSize
		}
	}
	if err := m.localipmanager.UpdateLocalIPs(iface); err != nil {
		return nil, fmt.Errorf("update local IPs: %w", err)
	}
	m.fragments = newFragmentTracker(m.logger)

	if disableConntrack {
		log.Info("conntrack is disabled")
	} else {
		m.udpTracker = conntrack.NewUDPTracker(conntrack.DefaultUDPTimeout, m.logger, flowLogger)
		m.icmpTracker = conntrack.NewICMPTracker(conntrack.DefaultICMPTimeout, m.logger, flowLogger)
		m.tcpTracker = conntrack.NewTCPTracker(conntrack.DefaultTCPTimeout, m.logger, flowLogger)
	}
	if m.netstack && m.localForwarding {
		if err := m.initForwarder(); err != nil {
			log.Errorf("failed to initialize forwarder: %v", err)
		}
	}
	if err := iface.SetFilter(m); err != nil {
		m.fragments.Close()
		return nil, fmt.Errorf("set filter: %w", err)

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Ensure the WireGuard interface exists and has its address assigned before constructing the uspfilter manager; pass a fully initialized IFaceMapper
  2. Guard UpdateLocalIPs against a nil iface and an invalid Address().IP (skip or error early with a clear message)
  3. Log the recovered panic value at error level with a stack so the origin inside processInterface is visible
  4. Add a constructor-time sanity check: if iface == nil || !iface.Address().IP.IsValid() return a descriptive error before calling UpdateLocalIPs

Example fix

// before
if err := m.localipmanager.UpdateLocalIPs(iface); err != nil {
    return nil, fmt.Errorf("update local IPs: %w", err)
}
// after - fail with intent when the interface snapshot cannot be trusted
if iface == nil || !iface.Address().IP.IsValid() {
    return nil, fmt.Errorf("iface address not ready for local IP snapshot")
}
if err := m.localipmanager.UpdateLocalIPs(iface); err != nil {
    return nil, fmt.Errorf("update local IPs: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

// construct the firewall manager only with a ready interface
if wgIface == nil || !wgIface.Address().IP.IsValid() {
    return nil, fmt.Errorf("cannot create firewall manager: interface address not ready")
}
fw, err := uspfilter.Create(wgIface, ...)

Type guard

func ifaceReadyForUspfilter(i common.IFaceMapper) bool {
    return i != nil && i.Address() != nil && i.Address().IP.IsValid()
}

Try / catch

fw, err := uspfilter.Create(...)
if err != nil {
    if strings.Contains(err.Error(), "update local IPs") {
        // interface snapshot failed; retry once after bring-up settles
        time.Sleep(settleDelay)
        fw, err = uspfilter.Create(...)
    }
    if err != nil {
        return fmt.Errorf("firewall init: %w", err)
    }
}

Prevention

When it happens

Trigger: Creating the uspfilter manager with a nil or half-initialized common.IFaceMapper whose Address() dereferences nil; WGIface created but address not yet assigned (Address().IP zero/invalid) when the firewall manager is constructed; a net.Interface with malformed flags/addresses triggering a panic in processInterface.

Common situations: Construction ordering bugs where the firewall manager is built before the overlay interface is up; embedded/wasm netstack paths where the iface wrapper differs; races between interface setup and ACL initialization.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/bacef6df989d4543. Report an issue: GitHub.