netbirdio/netbird · error
add legacy routing rule: %w
Error message
add legacy routing rule: %w
What it means
Returned by AddNatRule (router_linux.go:689) when addLegacyRouteRule fails. Legacy mode is enabled via SetLegacyManagement for management servers predating route ACLs (the preceding log.Warnf says all traffic is allowed for the destination), and addLegacyRouteRule (line 968) builds an unconditional ACCEPT forwarding rule in netbird-rt-fwd. The wrapped error is one of: applyNetwork source/destination failure (errors 697/698, i.e. ipset creation failing) or removeLegacyRouteRule failure (error 699) while replacing an existing rule.
Source
Thrown at client/firewall/nftables/router_linux.go:690
return fmt.Errorf("delete rule %s: %w", ruleKey, err)
}
delete(r.rules, ruleKey)
log.Debugf("removed route rule %s", ruleKey)
return nil
}
// AddNatRule appends a nftables rule pair to the nat chain
func (r *router) AddNatRule(pair firewall.RouterPair) error {
if err := r.refreshRulesMap(); err != nil {
return fmt.Errorf(refreshRulesMapError, err)
}
if r.legacyManagement {
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)
}
View on GitHub (pinned to 93e97f4bf1)
Solutions
- Fix the underlying cause using errors 697/698/699 guidance — this wrapper only re-labels those failures.
- Upgrade the management service so the agent leaves legacy mode (GetLegacyManagement()/SetLegacyManagement), replacing the allow-all rule with proper ACL enforcement.
- Reduce the routed network's prefix count (merge ranges) so set creation succeeds.
- Restart the agent to clear stale handle-less rules if 699 keeps firing.
Example fix
// before
if r.legacyManagement {
if err := r.addLegacyRouteRule(pair); err != nil {
return fmt.Errorf("add legacy routing rule: %w", err)
}
}
// after
if r.legacyManagement {
if err := r.addLegacyRouteRule(pair); err != nil {
// legacy allow-all is best-effort compat; surface cause but keep NAT path alive
log.Errorf("add legacy routing rule for %s: %v", pair.Destination, err)
if !isErrno(err, unix.ENOENT, unix.EEXIST) {
return fmt.Errorf("add legacy routing rule: %w", err)
}
}
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the pair before entering legacy mode rule creation
if r.legacyManagement {
if p := pair.Source.Prefix; p.IsValid() && p.Bits() > 0 || pair.Source.IsSet() {
// ok
} else {
return fmt.Errorf("invalid legacy source network %v", pair.Source)
}
} Type guard
func isApplyNetworkErr(err error) bool {
return strings.Contains(err.Error(), "apply ") || strings.Contains(err.Error(), "ipset")
} Try / catch
if err := r.addLegacyRouteRule(pair); err != nil {
if isApplyNetworkErr(err) && isErrno(err, unix.EEXIST, unix.ENOENT) {
// set-state issue; refresh and one retry usually clears it
_ = r.refreshRulesMap()
if rerr := r.addLegacyRouteRule(pair); rerr == nil {
return nil
}
}
return fmt.Errorf("add legacy routing rule: %w", err)
} Prevention
- Upgrade management past the route-ACL era so legacy mode is never enabled (SetLegacyManagement stays false).
- Keep legacy route networks small and merged; legacy rules share the same set machinery as ACLs.
- Log loudly when legacy mode is active — allow-all forwarding deserves visibility.
- Test mixed-version fleets (new agent + old management) in CI to catch legacy path regressions.
When it happens
Trigger: A NetBird peer managed by an old management version receiving a routed network whose source or destination expands into a prefix set that fails to create (>1500 prefixes, overlapping intervals, EEXIST leftovers), or re-applying a legacy route whose previous flush failed leaving a handle-less rule.
Common situations: Mixed-version fleets where a modern agent talks to a pre-route-ACL management; migrations where legacy rules are re-added on every network-map update; large routing ranges hitting set limits in legacy mode.
Related errors
- remove legacy routing rule: %w
- add nat rule: %w
- add inverse nat rule: %w
- insert rules for %s: %w
- remove prerouting rule: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/e66b4d77a06d3945.
Report an issue: GitHub.