netbirdio/netbird · error

flush error: %w

Error message

flush error: %w

What it means

Returned by createIpSet (router_linux.go:564) when the kernel rejects the batch containing the NEWSET creation and its initial elements. This is where AddSet's queued message actually meets the kernel: typical errnos are EEXIST (a set with the same name already exists in the work table), ENOTSUPP/EOPNOTSUPP (kernel or module without nftables interval sets), EINVAL (key type or flag mismatch for the table family), and EPERM (missing CAP_NET_ADMIN). On failure the function returns nil, so the refcounter's Increment fails and the caller's rule creation aborts.

Source

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

		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)
		}
		log.Debugf("Added new prefixes (%d) in ipset: %s", nSubPrefixes, setName)
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. On EEXIST, fetch and reuse the existing set instead of failing: conn.GetSetByName(r.workTable, setName) and continue with it (or DelSet it first for a clean slate).
  2. Verify the daemon runs as root with CAP_NET_ADMIN (check `grep CapEff /proc/self/status`).
  3. Confirm nf_tables support: `nft list ruleset` must work on the same host/namespace.
  4. Ensure the table family matches the set KeyType (ip table -> 4-byte keys, ip6 -> 16-byte) to avoid EINVAL.

Example fix

// before
if err := r.conn.Flush(); err != nil {
    return nil, fmt.Errorf("flush error: %w", err)
}

// after
if err := r.conn.Flush(); err != nil {
    if isErrno(err, unix.EEXIST) {
        if existing, gerr := r.conn.GetSetByName(r.workTable, setName); gerr == nil {
            log.Debugf("reusing existing set %s", setName)
            return existing, nil
        }
    }
    return nil, fmt.Errorf("flush error: %w", err)
}
Defensive patterns

Strategy: fallback

Validate before calling

// Pre-check for a leftover set with the same name before creating
if existing, err := r.conn.GetSetByName(r.workTable, setName); err == nil && existing != nil {
    log.Debugf("set %s already exists, reusing", setName)
    return existing, nil
}

Type guard

func isSetExistsErr(err error) bool {
	return isErrno(err, unix.EEXIST)
}

Try / catch

if err := r.conn.Flush(); err != nil {
    if isSetExistsErr(err) {
        if existing, gerr := r.conn.GetSetByName(r.workTable, setName); gerr == nil {
            return existing, nil // adopt existing set instead of failing
        }
    }
    if isErrno(err, unix.EPERM, unix.ENOTSUPP) {
        return nil, fmt.Errorf("flush error: %w", err) // environment problem, stop
    }
    return nil, fmt.Errorf("flush error: %w", err)
}

Prevention

When it happens

Trigger: First route rule using a prefix set after an unclean agent exit left the set in the work table; agent without root/CAP_NET_ADMIN; container on a kernel with nf_tables masked; creating an interval set on an ancient kernel (pre-4.x) without interval support.

Common situations: Restart loops where each run recreates sets that the previous crashed run left behind; Docker/LXC guests with restricted netlink; hosts hardened to drop nftables capability; family confusion when a v6 network reaches a v4 router.

Related errors


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