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

  1. Avoid flushing or editing NetBird's firewall chains externally; let the agent own its ruleset
  2. Restart the agent (or trigger a full reconnect) so it reconciles from scratch - the manager rebuilds route rules from the network map
  3. 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

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


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/0f93059141ec0683. Report an issue: GitHub.