netbirdio/netbird · warning
delete route rule: %w
Error message
delete route rule: %w
What it means
Appended in applyRouteACLs when firewall.DeleteRouteRule(id) fails while removing route ACL rules that disappeared from the new network map. The manager reconciles d.routeRules against newRouteRules after every update; a delete failure means the backend no longer has the rule under that ID or cannot remove it - e.g. the ruleset was flushed externally, the nftables/iptables table was replaced, or the rule was already garbage-collected by a backend restart.
Source
Thrown at client/internal/acl/manager.go:229
// Apply new rules - firewall manager will return existing rule ID if already present
for _, rule := range rules {
id, err := d.applyRouteACL(rule, dynamicResolver)
if err != nil {
if errors.Is(err, ErrSourceRangesEmpty) {
log.Debugf("skipping empty sources rule with destination %s: %v", rule.Destination, err)
} else {
merr = multierror.Append(merr, fmt.Errorf("add route rule: %w", err))
}
continue
}
newRouteRules[id] = struct{}{}
}
// Clean up old firewall rules
for id := range d.routeRules {
if _, exists := newRouteRules[id]; !exists {
if err := d.firewall.DeleteRouteRule(id); err != nil {
merr = multierror.Append(merr, fmt.Errorf("delete route rule: %w", err))
}
// implicitly deleted from the map
}
}
d.routeRules = newRouteRules
return nberrors.FormatErrorOrNil(merr)
}
func (d *DefaultManager) applyRouteACL(rule *mgmProto.RouteFirewallRule, dynamicResolver bool) (id.RuleID, error) {
if len(rule.SourceRanges) == 0 {
return "", ErrSourceRangesEmpty
}
var sources []netip.Prefix
for _, sourceRange := range rule.SourceRanges {
source, err := netip.ParsePrefix(sourceRange)
if err != nil {View on GitHub (pinned to 93e97f4bf1)
Solutions
- Avoid flushing or editing NetBird's firewall chains externally; let the agent own its ruleset
- Restart the agent (or trigger a full reconnect) so it reconciles from scratch - the manager rebuilds route rules from the network map
- If it recurs, check for a second netbird instance or firewall daemon (firewalld/ufw) fighting over the tables
Defensive patterns
Strategy: try-catch
Try / catch
if err := d.firewall.DeleteRouteRule(id); err != nil {
if isRuleNotFoundError(err) { // backend reports it is already gone
log.Debugf("route rule %s already absent: %v", id, err)
} else {
merr = multierror.Append(merr, fmt.Errorf("delete route rule: %w", err))
}
} Prevention
- Never flush or edit the agent's firewall tables externally; use the management API to change policies
- Run one agent and disable competing firewall daemons on NetBird-managed hosts
- After any manual firewall intervention, restart the agent so it reconciles its rule maps from scratch
When it happens
Trigger: External `nft flush ruleset` / `iptables -F` while the agent runs; another NetBird instance or firewall tool (firewalld, ufw reload) rewriting tables; agent's backend manager re-initialized between updates losing rule handles.
Common situations: Operators manually flushing firewall rules on a host; concurrent firewall management tools; container images recreating network namespaces. Stale rules then linger or the map and backend diverge until the next full reconcile.
Related errors
- reset v6 acl manager: %w
- reset acl manager: %w
- add IP to ipset: %w
- failed to check rule: %w
- rule already exists
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/0f93059141ec0683.
Report an issue: GitHub.