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

  1. Fix the underlying cause using errors 697/698/699 guidance — this wrapper only re-labels those failures.
  2. Upgrade the management service so the agent leaves legacy mode (GetLegacyManagement()/SetLegacyManagement), replacing the allow-all rule with proper ACL enforcement.
  3. Reduce the routed network's prefix count (merge ranges) so set creation succeeds.
  4. 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

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


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