{"record":{"id":"e1017cbffbafa4b0","repo":"netbirdio/netbird","slug":"failed-to-delete-rule-s-v-w","errorCode":null,"errorMessage":"failed to delete rule: %s, %v: %w","messagePattern":"failed to delete rule: (.+?), (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/firewall/iptables/acl_linux.go","lineNumber":223,"sourceCode":"\t\t\t\treturn fmt.Errorf(\"delete ip from ipset: %w\", err)\n\t\t\t}\n\t\t\tdelete(ipsetList.ips, r.ip)\n\t\t}\n\n\t\t// if after delete, set still contains other IPs,\n\t\t// no need to delete firewall rule and we should exit here\n\t\tif len(ipsetList.ips) != 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\t// we delete last IP from the set, that means we need to delete\n\t\t// set itself and associated firewall rule too\n\t\tm.ipsetStore.deleteIpset(r.ipsetName)\n\t\tshouldDestroyIpset = true\n\t}\n\n\tif err := m.iptablesClient.Delete(tableName, r.chain, r.specs...); err != nil {\n\t\treturn fmt.Errorf(\"failed to delete rule: %s, %v: %w\", r.chain, r.specs, err)\n\t}\n\n\tif r.mangleSpecs != nil {\n\t\tif err := m.iptablesClient.Delete(tableMangle, chainRTPRE, r.mangleSpecs...); err != nil {\n\t\t\tlog.Errorf(\"failed to delete mangle rule: %v\", err)\n\t\t}\n\t}\n\n\tif shouldDestroyIpset {\n\t\tif err := m.destroyIPSet(r.ipsetName); err != nil {\n\t\t\tif errors.Is(err, ipset.ErrBusy) || errors.Is(err, ipset.ErrSetNotExist) {\n\t\t\t\tlog.Debugf(\"destroy empty ipset: %v\", err)\n\t\t\t} else {\n\t\t\t\tlog.Errorf(\"destroy empty ipset: %v\", err)\n\t\t\t}\n\t\t}\n\t}\n","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/firewall/iptables/acl_linux.go#L205-L241","documentation":"iptablesClient.Delete of the filter-table rule failed. go-iptables' Delete is not idempotent: deleting a rule that is already absent returns 'Bad rule (does a matching rule exist in that chain?)' as an error (exit 1), unlike DeleteIfExists used elsewhere in this file. Failure therefore usually means kernel/memory desync, the referenced ipset vanished so the match clause cannot resolve, the chain was removed, or a privilege/lock problem.","triggerScenarios":"DeletePeerRule when the kernel rule was already removed externally but the manager still tracks it; deleting the last IP of a set whose earlier partial cleanup removed the rule; another process holding the xtables lock; non-root execution.","commonSituations":"Double-invoked cleanup (caller retries after a timeout, first attempt actually succeeded); external firewall flushing between manager start and delete; crash recovery where persisted rules no longer match the kernel.","solutions":["Use iptablesClient.DeleteIfExists for the ACL rule deletion, mirroring the mangle path in the same function.","When keeping Delete, match the go-iptables *Error with ExitStatus()==1 and treat it as success.","Reconcile state via Reset before retrying deletes after an unclean shutdown.","Ensure root and no concurrent xtables.lock holders."],"exampleFix":"// before\nif err := m.iptablesClient.Delete(tableName, r.chain, r.specs...); err != nil {\n    return fmt.Errorf(\"failed to delete rule: %s, %v: %w\", r.chain, r.specs, err)\n}\n\n// after\nif err := m.iptablesClient.DeleteIfExists(tableName, r.chain, r.specs...); err != nil {\n    return fmt.Errorf(\"failed to delete rule: %s, %v: %w\", r.chain, r.specs, err)\n}","handlingStrategy":"fallback","validationCode":"// idempotent pre-check: only call Delete when the rule is present\nif ok, err := iptClient.Exists(tableName, r.chain, r.specs...); err == nil && !ok {\n    // rule already absent; skip the delete and proceed to ipset teardown\n}","typeGuard":null,"tryCatchPattern":"if err := mgr.DeletePeerRule(rule); err != nil {\n    var ee *iptables.Error\n    if errors.As(err, &ee) && ee.ExitStatus() == 1 && strings.Contains(ee.Error(), \"Bad rule\") {\n        // rule already gone: idempotent success\n        err = nil\n    }\n}","preventionTips":["Prefer the manager's DeleteIfExists-style paths (or contribute one for the ACL rule) so deletes are idempotent.","Do not retry delete calls blindly after timeouts; check Exists first since the first attempt often succeeded.","Reconcile manager state with Reset after crashes instead of replaying stale deletes.","Keep external firewall manipulation off hosts running the agent."],"tags":["go","linux","iptables","firewall","acl","idempotency","netbird"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}