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
- Retry UpdateSet with the same set and complete prefix list - the sync is idempotent
- Inspect the v6 set as root: ipset list <name> (v6 sets carry the -v6 suffix convention in the ACL layer)
- 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
- Keep the full prefix list around so UpdateSet can be retried wholesale
- Remember v6 sets are only touched when v6 prefixes exist and the manager has v6
- Verify ipset membership after retries during large dynamic-route updates
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
- add v6 NAT rule: %w
- add IP to ipset: %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/aaa77b8064119d2d.
Report an issue: GitHub.