netbirdio/netbird · error

update v6 set: %w

Error message

update v6 set: %w

What it means

UpdateSet splits the prefix list by family; the v4 refresh (router.UpdateSet) succeeded, then the v6 refresh (router6.UpdateSet, an ipset member sync) failed and got this wrap. The result is partial state: v4 ipset members are current, v6 members are stale. The operation is idempotent - re-calling UpdateSet with the same set and full prefix list re-syncs both families.

Source

Thrown at client/firewall/iptables/manager_linux.go:458

	m.mutex.Lock()
	defer m.mutex.Unlock()

	var v4Prefixes, v6Prefixes []netip.Prefix
	for _, p := range prefixes {
		if p.Addr().Is6() {
			v6Prefixes = append(v6Prefixes, p)
		} else {
			v4Prefixes = append(v4Prefixes, p)
		}
	}

	if err := m.router.UpdateSet(set, v4Prefixes); err != nil {
		return err
	}

	if m.hasIPv6() && len(v6Prefixes) > 0 {
		if err := m.router6.UpdateSet(set, v6Prefixes); err != nil {
			return fmt.Errorf("update v6 set: %w", err)
		}
	}

	return nil
}

// AddInboundDNAT adds an inbound DNAT rule redirecting traffic from NetBird peers to local services.
func (m *Manager) AddInboundDNAT(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 inbound DNAT: %w", firewall.ErrIPv6NotInitialized)
		}
		return m.router6.AddInboundDNAT(localAddr, protocol, originalPort, translatedPort)
	}
	return m.router.AddInboundDNAT(localAddr, protocol, originalPort, translatedPort)

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Retry UpdateSet with the same set and complete prefix list - the sync is idempotent
  2. Inspect the v6 set as root: ipset list <name> (v6 sets carry the -v6 suffix convention in the ACL layer)
  3. If persistent, restart the agent to recreate the sets and re-apply membership

Example fix

// before
if err := mgr.UpdateSet(set, prefixes); err != nil {
	return err // v4 updated, v6 stale
}

// after
if err := mgr.UpdateSet(set, prefixes); err != nil {
	log.Warnf("set update failed, retrying once: %v", err)
	if rerr := mgr.UpdateSet(set, prefixes); rerr != nil {
		return fmt.Errorf("retry update set: %w", rerr)
	}
}
Defensive patterns

Strategy: retry

Try / catch

if err := mgr.UpdateSet(set, prefixes); err != nil {
    // v4 half applied; re-sync both families idempotently
    if rerr := mgr.UpdateSet(set, prefixes); rerr != nil {
        return fmt.Errorf("update set (retry): %w", rerr)
    }
    log.Debugf("v6 set resynced after retry")
}

Prevention

When it happens

Trigger: UpdateSet with at least one v6 prefix on a hasIPv6() manager where the v6 ipset operation fails - set destroyed underneath, kernel ipset module issues, or netfilter mutex contention.

Common situations: External ipset flush between calls; large dynamic route updates where DNS resolution changes membership mid-sync; hosts with ipset support but degraded v6 netfilter.

Related errors


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