netbirdio/netbird · error

destroy set %s: %w

Error message

destroy set %s: %w

What it means

Returned by router.deleteIpSet when ipset.Destroy fails. Unlike the iptables calls (DeleteIfExists), this destroy is not existence-tolerant. The kernel refuses to destroy a set that is still referenced by an iptables rule ('Set cannot be destroyed: set is in use'), and the library errors if the set does not exist or the caller lacks CAP_NET_ADMIN.

Source

Thrown at client/firewall/iptables/router_linux.go:258

}

func (r *router) createIpSet(setName string, sources []netip.Prefix) error {
	if err := r.createIPSet(setName); err != nil {
		return fmt.Errorf("create set %s: %w", setName, err)
	}

	for _, prefix := range sources {
		if err := r.addPrefixToIPSet(setName, prefix); err != nil {
			return fmt.Errorf("add element to set %s: %w", setName, err)
		}
	}

	return nil
}

func (r *router) deleteIpSet(setName string) error {
	if err := r.destroyIPSet(setName); err != nil {
		return fmt.Errorf("destroy set %s: %w", setName, err)
	}

	log.Debugf("Deleted unused ipset %s", setName)
	return nil
}

// AddNatRule inserts an iptables rule pair into the nat chain
func (r *router) AddNatRule(pair firewall.RouterPair) error {
	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 {
		return nil
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Find and delete referencing rules first: sudo iptables-save | grep 'match-set' then delete them
  2. Verify references: sudo ipset list <name> -t (Refs: field) or ipset list -tree
  3. Ensure only one agent instance runs on the host
  4. Re-run 'sudo netbird down' or 'netbird up --debug' after clearing stale references so teardown completes
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm a set is unreferenced before expecting destroy to succeed
sudo ipset list <name> -t   # Refs: must be 0
sudo iptables-save | grep -- "match-set <name>"

Try / catch

err := r.deleteIpSet(name)
if err != nil {
	if strings.Contains(err.Error(), "does not exist") {
		return nil // already gone; teardown converges
	}
	if strings.Contains(err.Error(), "in use") {
		// sweep referencing iptables rules, then retry once
	}
	return fmt.Errorf("delete ipset %s: %w", name, err)
}

Prevention

When it happens

Trigger: The ipset refcounter reaches zero via ipsetCounter.Decrement (after DeleteRouteRule / removeNatRule / removeLegacyRouteRule) and calls deleteIpSet. It fails when an iptables rule outside the manager still matches with -m set --match-set <name>, when the set was already destroyed (double teardown, crash-restart race), or on permission loss.

Common situations: Unclean shutdown left a stale iptables rule referencing the set; an admin added a manual rule using NetBird's set names; two agents tearing down concurrently; running 'netbird down' after manually flushing ipsets.

Related errors


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