netbirdio/netbird · error
determine destination: %w
Error message
determine destination: %w
What it means
Returned by applyRouteACL when determineDestination fails. Inside determineDestination the realistic failure is `parse destination: %w` from netip.ParsePrefix(rule.Destination) for non-dynamic rules; the dynamic branches (IsDynamic with domains, or the outdated-management fallback to the default prefix) always succeed. So the error means a static route ACL rule carried a Destination that is not a parseable CIDR.
Source
Thrown at client/internal/acl/manager.go:255
}
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 {
return "", fmt.Errorf("parse source range: %w", err)
}
sources = append(sources, source)
}
destination, err := determineDestination(rule, dynamicResolver, sources)
if err != nil {
return "", fmt.Errorf("determine destination: %w", err)
}
protocol, err := convertToFirewallProtocol(rule.Protocol)
if err != nil {
return "", fmt.Errorf("invalid protocol: %w", err)
}
action, err := convertFirewallAction(rule.Action)
if err != nil {
return "", fmt.Errorf("invalid action: %w", err)
}
dPorts := convertPortInfo(rule.PortInfo)
addedRule, err := d.firewall.AddRouteFiltering(rule.PolicyID, sources, destination, protocol, nil, dPorts, action)
if err != nil {
return "", fmt.Errorf("add route rule: %w", err)
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Set Destination to a valid CIDR (a.b.c.d/32 for single hosts), or move the target into the domains field and enable the dynamic flag
- Verify the management version validates route firewall destinations before dispatch; upgrade if not
- Reconnect the peer / update the policy afterwards so the agent re-applies the corrected rule
Defensive patterns
Strategy: validation
Validate before calling
// policy-authoring guard: destination must be a CIDR unless the rule is dynamic
func validRouteDestination(r *mgmProto.RouteFirewallRule) error {
if r.IsDynamic {
if len(r.Domains) == 0 {
return errors.New("dynamic rule without domains")
}
return nil
}
if _, err := netip.ParsePrefix(r.Destination); err != nil {
return fmt.Errorf("destination %q must be a CIDR: %w", r.Destination, err)
}
return nil
} Type guard
func isStaticCidrDestination(r *mgmProto.RouteFirewallRule) bool {
return !r.IsDynamic && isStrictCIDR(r.Destination)
} Try / catch
destination, err := determineDestination(rule, dynamicResolver, sources)
if err != nil {
if strings.Contains(err.Error(), "parse destination") {
log.Warnf("rule %s has non-CIDR destination %q; fix policy or set IsDynamic", rule.PolicyID, rule.Destination)
return "", err // drop rule rather than guess at an over-broad destination
}
return "", err
} Prevention
- Domains belong in the domains field with IsDynamic=true, never in Destination
- Express single hosts as /32 or /128 CIDRs
- Upgrade management so it validates route ACL destinations before dispatching them to agents
When it happens
Trigger: Rule with IsDynamic=false but a Destination holding a bare IP, hostname, or empty string; management/API writing a domain into Destination without setting IsDynamic; version mismatch where the agent expects CIDR-only destinations.
Common situations: Policies authored via API mixing up the domain field semantics (domains belong in rule.Domains with IsDynamic=true); dashboard edge cases; management validation gaps on older builds.
Related errors
- add route rule: %w
- parse source range: %w
- invalid protocol: %w
- add IP to ipset: %w
- failed to check rule: %w
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/74fca51413666ad5.
Report an issue: GitHub.