{"record":{"id":"e836cef8ac420347","repo":"netbirdio/netbird","slug":"flush-error-w","errorCode":null,"errorMessage":"flush error: %w","messagePattern":"flush error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/nftables/router_linux.go","lineNumber":565,"sourceCode":"\t\tName:    setName,\n\t\tComment: input.set.Comment(),\n\t\tTable:   r.workTable,\n\t\t// required for prefixes\n\t\tInterval: true,\n\t\tKeyType:  r.af.setKeyType,\n\t}\n\n\telements := r.convertPrefixesToSet(prefixes)\n\tnElements := len(elements)\n\n\tmaxElements := maxPrefixesSet * 2\n\tinitialElements := elements[:min(maxElements, nElements)]\n\n\tif err := r.conn.AddSet(nfset, initialElements); err != nil {\n\t\treturn nil, fmt.Errorf(\"error adding set %s: %w\", setName, err)\n\t}\n\tif err := r.conn.Flush(); err != nil {\n\t\treturn nil, fmt.Errorf(\"flush error: %w\", err)\n\t}\n\tlog.Debugf(\"Created new ipset: %s with %d initial prefixes (total prefixes %d)\", setName, len(initialElements)/2, len(prefixes))\n\n\tvar subEnd int\n\tfor subStart := maxElements; subStart < nElements; subStart += maxElements {\n\t\tsubEnd = min(subStart+maxElements, nElements)\n\t\tsubElement := elements[subStart:subEnd]\n\t\tnSubPrefixes := len(subElement) / 2\n\t\tlog.Tracef(\"Adding new prefixes (%d) in ipset: %s\", nSubPrefixes, setName)\n\t\tif err := r.conn.SetAddElements(nfset, subElement); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error adding prefixes (%d) to set %s: %w\", nSubPrefixes, setName, err)\n\t\t}\n\t\tif err := r.conn.Flush(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"flush error: %w\", err)\n\t\t}\n\t\tlog.Debugf(\"Added new prefixes (%d) in ipset: %s\", nSubPrefixes, setName)\n\t}\n","sourceCodeStart":547,"sourceCodeEnd":583,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/nftables/router_linux.go#L547-L583","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nif err := r.conn.Flush(); err != nil {\n    return nil, fmt.Errorf(\"flush error: %w\", err)\n}\n\n// after\nif err := r.conn.Flush(); err != nil {\n    if isErrno(err, unix.EEXIST) {\n        if existing, gerr := r.conn.GetSetByName(r.workTable, setName); gerr == nil {\n            log.Debugf(\"reusing existing set %s\", setName)\n            return existing, nil\n        }\n    }\n    return nil, fmt.Errorf(\"flush error: %w\", err)\n}","handlingStrategy":"fallback","validationCode":"// Pre-check for a leftover set with the same name before creating\nif existing, err := r.conn.GetSetByName(r.workTable, setName); err == nil && existing != nil {\n    log.Debugf(\"set %s already exists, reusing\", setName)\n    return existing, nil\n}","typeGuard":"func isSetExistsErr(err error) bool {\n\treturn isErrno(err, unix.EEXIST)\n}","tryCatchPattern":"if err := r.conn.Flush(); err != nil {\n    if isSetExistsErr(err) {\n        if existing, gerr := r.conn.GetSetByName(r.workTable, setName); gerr == nil {\n            return existing, nil // adopt existing set instead of failing\n        }\n    }\n    if isErrno(err, unix.EPERM, unix.ENOTSUPP) {\n        return nil, fmt.Errorf(\"flush error: %w\", err) // environment problem, stop\n    }\n    return nil, fmt.Errorf(\"flush error: %w\", err)\n}","preventionTips":["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."],"tags":["go","nftables","netlink","ipset","firewall","linux"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}