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
- 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).
- Verify the daemon runs as root with CAP_NET_ADMIN (check `grep CapEff /proc/self/status`).
- Confirm nf_tables support: `nft list ruleset` must work on the same host/namespace.
- 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
- Tear down and recreate the work table on agent initialization so stale sets from crashed runs disappear.
- Verify CAP_NET_ADMIN and nf_tables availability in health checks before route programming starts.
- Keep initial element payloads modest; the kernel failure point (~1638 prefixes) is documented in the code.
- Log the set name and element count at creation so EEXIST incidents can be correlated with prior runs.
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
- error adding set %s: %w
- error adding prefixes (%d) to set %s: %w
- create ipset: %w
- delete ip from ipset: %w
- add IP to ipset %s: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/e836cef8ac420347.
Report an issue: GitHub.