netbirdio/netbird · error

error adding set %s: %w

Error message

error adding set %s: %w

What it means

Returned by createIpSet (router_linux.go:561) when conn.AddSet fails while creating the interval set that backs a multi-prefix network. AddSet validates and queues the NEWSET message plus up to 3000 initial elements (maxPrefixesSet=1500 prefixes x 2 half-open interval endpoints, lines 558-559); construction-time failures include a nil Table, a KeyType whose byte length does not match the table family (r.af.setKeyType), or malformed set flags. Kernel rejections for the queued message surface one line later at Flush (error 683).

Source

Thrown at client/firewall/nftables/router_linux.go:562

	prefixes := firewall.MergeIPRanges(input.prefixes)

	nfset := &nftables.Set{
		Name:    setName,
		Comment: input.set.Comment(),
		Table:   r.workTable,
		// required for prefixes
		Interval: true,
		KeyType:  r.af.setKeyType,
	}

	elements := r.convertPrefixesToSet(prefixes)
	nElements := len(elements)

	maxElements := maxPrefixesSet * 2
	initialElements := elements[:min(maxElements, nElements)]

	if err := r.conn.AddSet(nfset, initialElements); err != nil {
		return nil, fmt.Errorf("error adding set %s: %w", setName, err)
	}
	if err := r.conn.Flush(); err != nil {
		return nil, fmt.Errorf("flush error: %w", err)
	}
	log.Debugf("Created new ipset: %s with %d initial prefixes (total prefixes %d)", setName, len(initialElements)/2, len(prefixes))

	var subEnd int
	for subStart := maxElements; subStart < nElements; subStart += maxElements {
		subEnd = min(subStart+maxElements, nElements)
		subElement := elements[subStart:subEnd]
		nSubPrefixes := len(subElement) / 2
		log.Tracef("Adding new prefixes (%d) in ipset: %s", nSubPrefixes, setName)
		if err := r.conn.SetAddElements(nfset, subElement); err != nil {
			return nil, fmt.Errorf("error adding prefixes (%d) to set %s: %w", nSubPrefixes, setName, err)
		}
		if err := r.conn.Flush(); err != nil {
			return nil, fmt.Errorf("flush error: %w", err)
		}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Confirm the set's KeyType matches the router's address family (4-byte keys for the ip table, 16-byte for ip6) — check r.af.setKeyType wiring.
  2. If a stale set with the same HashedName exists from a previous run, delete the work table or DelSet it before re-adding (the manager recreates the table on init).
  3. Verify set invariants the library requires: non-nil Table (r.workTable), Interval=true for prefixes, and a non-empty unique Name.
  4. Check the wrapped error before Flush-related codes — if it is a marshaling/validation error it will reproduce deterministically, so fix construction instead of retrying.

Example fix

// before
nfset := &nftables.Set{Name: setName, Table: r.workTable, Interval: true, KeyType: r.af.setKeyType}
if err := r.conn.AddSet(nfset, initialElements); err != nil {
    return nil, fmt.Errorf("error adding set %s: %w", setName, err)
}

// after
nfset := &nftables.Set{Name: setName, Table: r.workTable, Interval: true, KeyType: r.af.setKeyType}
if nfset.KeyType == 0 || nfset.KeyType.Length != r.af.addrLen {
    return nil, fmt.Errorf("set %s key length %d does not match family %d", setName, nfset.KeyType.Length, r.af.addrLen)
}
if err := r.conn.AddSet(nfset, initialElements); err != nil {
    return nil, fmt.Errorf("error adding set %s: %w", setName, err)
}
Defensive patterns

Strategy: fallback

Validate before calling

// Validate set construction matches the table family before AddSet
wantLen := uint32(4)
if r.af.tableFamily == nftables.TableFamilyIPv6 {
    wantLen = 16
}
if nfset.Table == nil || nfset.KeyType.Length != wantLen || !nfset.Interval {
    return nil, fmt.Errorf("invalid set definition %s: table=%v keyLen=%d interval=%v", setName, nfset.Table, nfset.KeyType.Length, nfset.Interval)
}

Type guard

func validSetForFamily(s *nftables.Set, family nftables.TableFamily) bool {
	if s == nil || s.Table == nil || !s.Interval {
		return false
	}
	want := uint32(4)
	if family == nftables.TableFamilyIPv6 {
		want = 16
	}
	return s.KeyType != nil && s.KeyType.Length == want
}

Try / catch

if err := r.conn.AddSet(nfset, initialElements); err != nil {
    // construction error: deterministic, do not retry blindly
    if existing, gerr := r.conn.GetSetByName(r.workTable, setName); gerr == nil {
        return existing, nil // fall back to the existing set
    }
    return nil, fmt.Errorf("error adding set %s: %w", setName, err)
}

Prevention

When it happens

Trigger: Calling AddRouteFiltering/getIpSet with a firewall.Set whose hashed name collides with an incompatible leftover set, an IPv6 set created under an IPv4 router (or vice versa via familyForAddr mismatch), or an element key length that disagrees with KeyType during marshaling.

Common situations: Agent restart after an unclean shutdown leaving a set with the same hash name but different key type; dual-stack peers where the v4 and v6 routers build sets from the same prefix list; embedded/minimal kernels missing interval-set support (flags rejected).

Related errors


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