netbirdio/netbird · error
insert rules for %s: %w
Error message
insert rules for %s: %w
What it means
Returned by AddNatRule (router_linux.go:704-706) when the final Conn.Flush fails that would commit the legacy forwarding rule and/or the two mangle-prerouting mark rules for the pair. Before returning, rollbackRules (line 713) removes the three keys from r.rules and decrements their set counters, restoring local bookkeeping while the kernel (which applies a failed netlink batch atomically) typically keeps its prior state. Errnos seen here: EINVAL from a malformed expression, ENOENT when the target chain/table vanished (external flush), EPERM without CAP_NET_ADMIN.
Source
Thrown at client/firewall/nftables/router_linux.go:706
log.Warnf("This peer is connected to a NetBird Management service with an older version. Allowing all traffic for %s", pair.Destination)
if err := r.addLegacyRouteRule(pair); err != nil {
return fmt.Errorf("add legacy routing rule: %w", err)
}
}
if pair.Masquerade {
if err := r.addNatRule(pair); err != nil {
return fmt.Errorf("add nat rule: %w", err)
}
if err := r.addNatRule(firewall.GetInversePair(pair)); err != nil {
return fmt.Errorf("add inverse nat rule: %w", err)
}
}
if err := r.conn.Flush(); err != nil {
r.rollbackRules(pair)
return fmt.Errorf("insert rules for %s: %w", pair.Destination, err)
}
return nil
}
// rollbackRules cleans up unflushed rules and their set counters after a flush failure.
func (r *router) rollbackRules(pair firewall.RouterPair) {
keys := []string{
firewall.GenKey(firewall.ForwardingFormat, pair),
firewall.GenKey(firewall.PreroutingFormat, pair),
firewall.GenKey(firewall.PreroutingFormat, firewall.GetInversePair(pair)),
}
for _, key := range keys {
rule, ok := r.rules[key]
if !ok {
continue
}
if err := r.decrementSetCounter(rule); err != nil {View on GitHub (pinned to 93e97f4bf1)
Solutions
- Inspect the errno: EPERM -> fix capabilities; ENOENT -> recreate the work table via manager init, then retry; EINVAL -> capture the rule expressions in a bug report.
- Retry AddNatRule after refreshRulesMap — rollbackRules already restored the map so the retry rebuilds cleanly.
- Ensure table/chain creation (createContainers) succeeded before route rules are attempted.
- Keep external nftables mutation away from the netbird table during updates.
Example fix
// before
if err := r.conn.Flush(); err != nil {
r.rollbackRules(pair)
return fmt.Errorf("insert rules for %s: %w", pair.Destination, err)
}
// after
if err := r.conn.Flush(); err != nil {
r.rollbackRules(pair)
if isErrno(err, unix.ENOENT) {
// chains were externally flushed; recreate containers and retry once
if cerr := r.createContainers(); cerr == nil {
return r.AddNatRule(pair)
}
}
return fmt.Errorf("insert rules for %s: %w", pair.Destination, err)
} Defensive patterns
Strategy: fallback
Validate before calling
// Pre-flight: the target chains must exist before committing rules
for _, name := range []string{chainNameManglePrerouting, chainNameRoutingFw} {
if r.chains[name] == nil {
return fmt.Errorf("chain %s missing; recreate containers first", name)
}
} Type guard
func isChainMissingErr(err error) bool {
return isErrno(err, unix.ENOENT)
} Try / catch
if err := r.conn.Flush(); err != nil {
r.rollbackRules(pair)
if isChainMissingErr(err) {
if cerr := r.createContainers(); cerr == nil {
return r.AddNatRule(pair) // rebuilt chains, retry once
}
}
return fmt.Errorf("insert rules for %s: %w", pair.Destination, err)
} Prevention
- rollbackRules already restores local state on flush failure — keep calling it before any retry so maps stay consistent.
- Verify chain/table existence after any external firewall event (firewalld reload) before programming rules.
- Run the agent with a systemd unit that holds CAP_NET_ADMIN permanently; mid-flight drops surface as EPERM here.
- Consider nftables batches atomic: a failed flush usually leaves kernel state untouched, so a clean retry is safe.
When it happens
Trigger: Committing a masqueraded route while the work table was flushed externally (ENOENT on netbird-rt-fwd / mangle chains); queueing a rule whose expressions fail kernel validation (EINVAL); running the agent without CAP_NET_ADMIN at rule-commit time.
Common situations: Route updates racing firewalld reloads; first route after agent start where table init partially failed (createContainers error was swallowed earlier); capability drops in hardened systemd units.
Related errors
- add nat rule: %w
- add inverse nat rule: %w
- remove prerouting rule: %w
- decrement set counter: %w
- error adding set %s: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/e5fd8527a633e66e.
Report an issue: GitHub.