netbirdio/netbird · error

source: %w

Error message

source: %w

What it means

Returned by router.applyNetwork (client/firewall/nftables/router_linux.go:2121) when a Network-Set source cannot be turned into nftables expressions. It wraps getIpSet, which increments a refcounted nft Set (ipsetCounter.Increment) and can fail while creating the set ('create or get ipset') or building the lookup expressions - e.g. set with prefixes of the wrong address family for the table, overlapping/unmergeable intervals, oversized element batches, or a netlink failure on AddSet/Flush. The terse 'source:' prefix marks this as the source-side leg; the same helper is reused for destinations.

Source

Thrown at client/firewall/nftables/router_linux.go:2121

	}
	if err := r.conn.Flush(); err != nil {
		return fmt.Errorf("flush delete output DNAT rule: %w", err)
	}
	delete(r.rules, ruleID)

	return nil
}

// applyNetwork generates nftables expressions for networks (CIDR) or sets
func (r *router) applyNetwork(
	network firewall.Network,
	setPrefixes []netip.Prefix,
	isSource bool,
) ([]expr.Any, error) {
	if network.IsSet() {
		exprs, err := r.getIpSet(network.Set, setPrefixes, isSource)
		if err != nil {
			return nil, fmt.Errorf("source: %w", err)
		}
		return exprs, nil
	}

	if network.IsPrefix() {
		return r.applyPrefix(network.Prefix, isSource), nil
	}

	return nil, nil
}

// applyPrefix generates nftables expressions for a CIDR prefix
func (r *router) applyPrefix(prefix netip.Prefix, isSource bool) []expr.Any {
	// dst offset by default
	offset := r.af.dstAddrOffset
	if isSource {
		// src offset
		offset = r.af.srcAddrOffset

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Filter the Set's prefixes by address family before calling AddRouteRule so only prefixes matching r.af reach the table
  2. Ensure overlapping prefixes are merged (firewall.MergeIPRanges is applied in createIpSet) and that the same HashedName always maps to identical prefix contents
  3. If a stale set with incompatible flags exists, recreate the manager (Create) which rebuilds the work table, then re-add routes
  4. Batch-check element counts against maxPrefixesSet limits and split very large sets

Example fix

// before
exprs, err := r.getIpSet(network.Set, setPrefixes, isSource)
if err != nil {
    return nil, fmt.Errorf("source: %w", err)
}
// after - drop prefixes that do not belong to this table's family
compatible := filterPrefixesForFamily(setPrefixes, r.af.tableFamily == nftables.TableFamilyIPv4)
exprs, err := r.getIpSet(network.Set, compatible, isSource)
if err != nil {
    return nil, fmt.Errorf("source: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

// Keep only prefixes that match the router's family before adding a route
wantV4 := tableFamily == nftables.TableFamilyIPv4
compatible := make([]netip.Prefix, 0, len(setPrefixes))
for _, p := range setPrefixes {
    if p.Addr().Is4() == wantV4 {
        compatible = append(compatible, p.Masked())
    }
}
if len(compatible) == 0 {
    return fmt.Errorf("network set %s has no %s prefixes", set.Name(), family)
}

Type guard

func prefixesMatchFamily(ps []netip.Prefix, v4 bool) bool {
    for _, p := range ps {
        if p.Addr().Is4() != v4 {
            return false
        }
    }
    return len(ps) > 0
}

Try / catch

if _, err := router.AddRouteRule(net, sources, ...); err != nil {
    if strings.Contains(err.Error(), "source:") || strings.Contains(err.Error(), "ipset") {
        log.Warnf("network set %s unusable on this family, skipping route", net.ID)
        continue
    }
    return err
}

Prevention

When it happens

Trigger: A routing/ACL policy whose network Set contains prefixes that do not match the router's address family (v6 prefixes in the IPv4 table); two different Sets hashed to the same nft name with different contents; a Set so large the element batch exceeds netlink limits; Set created previously with incompatible flags (Interval/KeyType) by an older agent version.

Common situations: Management sends a mixed-family distribution group; duplicate network names colliding in HashedName; upgrading the agent while old nft sets persist from the previous version; concurrent route updates racing on the same set name.

Related errors


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