{"record":{"id":"acc2c9f8bea17a09","repo":"netbirdio/netbird","slug":"destroy-set-s-w","errorCode":null,"errorMessage":"destroy set %s: %w","messagePattern":"destroy set (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/iptables/router_linux.go","lineNumber":258,"sourceCode":"}\n\nfunc (r *router) createIpSet(setName string, sources []netip.Prefix) error {\n\tif err := r.createIPSet(setName); err != nil {\n\t\treturn fmt.Errorf(\"create set %s: %w\", setName, err)\n\t}\n\n\tfor _, prefix := range sources {\n\t\tif err := r.addPrefixToIPSet(setName, prefix); err != nil {\n\t\t\treturn fmt.Errorf(\"add element to set %s: %w\", setName, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (r *router) deleteIpSet(setName string) error {\n\tif err := r.destroyIPSet(setName); err != nil {\n\t\treturn fmt.Errorf(\"destroy set %s: %w\", setName, err)\n\t}\n\n\tlog.Debugf(\"Deleted unused ipset %s\", setName)\n\treturn nil\n}\n\n// AddNatRule inserts an iptables rule pair into the nat chain\nfunc (r *router) AddNatRule(pair firewall.RouterPair) error {\n\tif r.legacyManagement {\n\t\tlog.Warnf(\"This peer is connected to a NetBird Management service with an older version. Allowing all traffic for %s\", pair.Destination)\n\t\tif err := r.addLegacyRouteRule(pair); err != nil {\n\t\t\treturn fmt.Errorf(\"add legacy routing rule: %w\", err)\n\t\t}\n\t}\n\n\tif !pair.Masquerade {\n\t\treturn nil\n\t}","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/iptables/router_linux.go#L240-L276","documentation":"Returned by router.deleteIpSet when ipset.Destroy fails. Unlike the iptables calls (DeleteIfExists), this destroy is not existence-tolerant. The kernel refuses to destroy a set that is still referenced by an iptables rule ('Set cannot be destroyed: set is in use'), and the library errors if the set does not exist or the caller lacks CAP_NET_ADMIN.","triggerScenarios":"The ipset refcounter reaches zero via ipsetCounter.Decrement (after DeleteRouteRule / removeNatRule / removeLegacyRouteRule) and calls deleteIpSet. It fails when an iptables rule outside the manager still matches with -m set --match-set <name>, when the set was already destroyed (double teardown, crash-restart race), or on permission loss.","commonSituations":"Unclean shutdown left a stale iptables rule referencing the set; an admin added a manual rule using NetBird's set names; two agents tearing down concurrently; running 'netbird down' after manually flushing ipsets.","solutions":["Find and delete referencing rules first: sudo iptables-save | grep 'match-set' then delete them","Verify references: sudo ipset list <name> -t (Refs: field) or ipset list -tree","Ensure only one agent instance runs on the host","Re-run 'sudo netbird down' or 'netbird up --debug' after clearing stale references so teardown completes"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// confirm a set is unreferenced before expecting destroy to succeed\nsudo ipset list <name> -t   # Refs: must be 0\nsudo iptables-save | grep -- \"match-set <name>\"","typeGuard":null,"tryCatchPattern":"err := r.deleteIpSet(name)\nif err != nil {\n\tif strings.Contains(err.Error(), \"does not exist\") {\n\t\treturn nil // already gone; teardown converges\n\t}\n\tif strings.Contains(err.Error(), \"in use\") {\n\t\t// sweep referencing iptables rules, then retry once\n\t}\n\treturn fmt.Errorf(\"delete ipset %s: %w\", name, err)\n}","preventionTips":["Always delete iptables rules referencing a set before destroying it","Run exactly one netbird instance per host","After failures, verify teardown with 'ipset list -t' and 'iptables-save | grep match-set'"],"tags":["iptables","ipset","cleanup","linux","netbird"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}