netbirdio/netbird · error
add route rule: %w
Error message
add route rule: %w
What it means
Appended per-rule in applyRouteACLs when applyRouteACL fails for a route-firewall (distribution policy) rule. It wraps every failure inside that helper: parse source range, determine destination, invalid protocol/action, and `add route rule` from firewall.AddRouteFiltering itself. ErrSourceRangesEmpty is deliberately excluded (skipped at debug), so seeing this error means a real parsing or backend problem. Failed rules are not inserted into newRouteRules, so the route ACL is not enforced for that traffic.
Source
Thrown at client/internal/acl/manager.go:218
}
delete(d.peerRulesPairs, pairID)
}
}
d.peerRulesPairs = newRulePairs
}
func (d *DefaultManager) applyRouteACLs(rules []*mgmProto.RouteFirewallRule, dynamicResolver bool) error {
newRouteRules := make(map[id.RuleID]struct{}, len(rules))
var merr *multierror.Error
// 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)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Unwrap the %w chain in the engine logs - the inner message names the exact field (source range, destination, protocol) or the backend
- Fix the offending policy field in management (valid CIDR, known protocol)
- Update the agent so its RuleProtocol/RuleAction enums cover what management emits
- Verify firewall backend health with `nft list ruleset` / `iptables-save` and agent capabilities
Defensive patterns
Strategy: try-catch
Validate before calling
// management-side guard for route firewall rules
func validRouteRule(r *mgmProto.RouteFirewallRule) error {
if len(r.SourceRanges) == 0 {
return errors.New("no source ranges")
}
for _, s := range r.SourceRanges {
if _, err := netip.ParsePrefix(s); err != nil {
return fmt.Errorf("source %q: %w", s, err)
}
}
if !r.IsDynamic {
if _, err := netip.ParsePrefix(r.Destination); err != nil {
return fmt.Errorf("destination %q: %w", r.Destination, err)
}
}
return nil
} Try / catch
if err := d.applyRouteACLs(rules, dyn); err != nil {
var merr *multierror.Error
if errors.As(err, &merr) {
for _, e := range merr.Errors {
switch {
case strings.Contains(e.Error(), "parse source range"),
strings.Contains(e.Error(), "determine destination"),
strings.Contains(e.Error(), "invalid protocol"):
log.Errorf("management sent malformed rule - fix policy: %v", e)
case strings.Contains(e.Error(), "add route rule"):
log.Errorf("firewall backend rejected rule: %v", e)
case strings.Contains(e.Error(), "delete route rule"):
log.Warnf("reconcile divergence: %v", e)
}
}
}
} Prevention
- Run the validRouteRule guard above in management before dispatching network maps
- Keep agent >= management version so enum and format expectations match
- Remember ErrSourceRangesEmpty rules are silently skipped by design - do not add sources later expecting old rules to apply
When it happens
Trigger: Management sending a RouteFirewallRule with any malformed field (bad CIDR in SourceRanges or Destination, protocol enum the agent does not know), or the firewall backend failing AddRouteFiltering (nftables/iptables error, full ruleset).
Common situations: Network routes (access control policies on routing groups) configured after a management upgrade while agents lag behind; API-authored policies with invalid CIDRs; hosts where the firewall backend is degraded.
Related errors
- add IP to ipset: %w
- failed to check rule: %w
- rule already exists
- failed to delete rule: %s, %v: %w
- add peer filtering for %s: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/ffbdb2568290ef5e.
Report an issue: GitHub.