netbirdio/netbird · error
failed to check rule: %w
Error message
failed to check rule: %w
What it means
go-iptables' Exists runs `iptables -t filter -C NETBIRD-ACL-INPUT <specs>`; exit status 1 means 'no match' (returns false,nil), but any other failure is returned as this error. Non-1 failures include xtables lock contention, a missing chain or table, unparseable/unsupported match extensions in the spec (e.g. `-m set --match-set <name>` when the referenced set or the xt_set match module is missing), and running as non-root (iptables exit 4).
Source
Thrown at client/firewall/iptables/acl_linux.go:152
log.Debugf("flush ipset %s before use: %v", ipsetName, err)
} else {
log.Errorf("flush ipset %s before use: %v", ipsetName, err)
}
}
if err := m.createIPSet(ipsetName); err != nil {
return nil, fmt.Errorf("create ipset: %w", err)
}
if err := m.addToIPSet(ipsetName, ip); err != nil {
return nil, fmt.Errorf("add IP to ipset: %w", err)
}
ipList := newIpList(ip.String())
m.ipsetStore.addIpList(ipsetName, ipList)
}
ok, err := m.iptablesClient.Exists(tableFilter, chain, specs...)
if err != nil {
return nil, fmt.Errorf("failed to check rule: %w", err)
}
if ok {
return nil, fmt.Errorf("rule already exists")
}
// Insert DROP rules at the beginning, append ACCEPT rules at the end
if action == firewall.ActionDrop {
// Insert at the beginning of the chain (position 1)
err = m.iptablesClient.Insert(tableFilter, chain, 1, specs...)
} else {
err = m.iptablesClient.Append(tableFilter, chain, specs...)
}
if err != nil {
return nil, err
}
if err := m.iptablesClient.Append(tableMangle, chainRTPRE, mangleSpecs...); err != nil {
log.Errorf("failed to add mangle rule: %v", err)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Run the agent as root; exit code 4 from iptables means permission denied.
- Ensure the kernel set referenced by --match-set exists before rule creation (it is created earlier in the same function, so an external destroyer is the usual culprit).
- Load required modules: `modprobe xt_set ip_set ip_set_hash_net`.
- For xtables lock contention, retry the AddPeerFiltering call once after a short backoff, or reduce concurrent iptables users on the host.
Defensive patterns
Strategy: try-catch
Validate before calling
func checkIptablesEnv() error {
if os.Geteuid() != 0 {
return errors.New("iptables requires root (exit status 4 otherwise)")
}
for _, b := range []string{"iptables"} {
if _, err := exec.LookPath(b); err != nil {
return fmt.Errorf("%s not in PATH: %w", b, err)
}
}
return nil
} Try / catch
if err := mgr.AddPeerFiltering(...); err != nil {
var ee *iptables.Error
if errors.As(err, &iptables.Error{}) || errors.As(err, &ee) {
// classify: exit 4 = permission, exit 2/3 = table/chain or lock issues
log.Errorf("iptables check failed with exit %d", ee.ExitStatus())
}
if strings.Contains(err.Error(), "failed to check rule") && isXtablesLockHeld() {
time.Sleep(250 * time.Millisecond) // then retry once
err = mgr.AddPeerFiltering(...)
}
} Prevention
- Always run the agent as root; iptables returns exit 4 for permission errors on every subcommand including -C.
- Load xt_set and ip_set modules on modular kernels so `-m set --match-set` specs are parseable.
- Avoid running competing iptables batch jobs (Docker daemon restarts, firewalld reloads) while the agent applies ACLs.
- Pin one iptables backend (legacy vs nft) system-wide so rules and checks see the same ruleset.
When it happens
Trigger: AddPeerFiltering when the spec references an ipset that does not exist in the kernel (set destroyed between addToIPSet and the Exists check); xt_set/xt_comment module not loaded on modular kernels; another process holding /run/xtables.lock while the agent checks; agent invoked without root so iptables refuses to list rules.
Common situations: Minimal container images without match extensions; iptables-legacy vs iptables-nft mixing where the rule lives in the other backend; hosts running heavy parallel iptables scripts (Docker, firewalld) causing transient lock errors.
Related errors
- add IP to ipset: %w
- rule already exists
- failed to delete rule: %s, %v: %w
- create ipset: %w
- list chains: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/95d409b116a2f6e8.
Report an issue: GitHub.