netbirdio/netbird · error

delete ip from ipset: %w

Error message

delete ip from ipset: %w

What it means

The netlink DEL of a peer IP from its ipset failed inside DeletePeerRule. The in-memory ips map guard above only proves the manager believes the element exists; the kernel set may disagree (element or whole set removed externally), which yields ErrNotExist from ipset-go. Other causes are family mismatch between the net.IP and the set, or missing CAP_NET_ADMIN.

Source

Thrown at client/firewall/iptables/acl_linux.go:205

}

// DeletePeerRule from the firewall by rule definition
func (m *aclManager) DeletePeerRule(rule firewall.Rule) error {
	r, ok := rule.(*Rule)
	if !ok {
		return fmt.Errorf("invalid rule type")
	}

	shouldDestroyIpset := false
	if ipsetList, ok := m.ipsetStore.ipset(r.ipsetName); ok {
		// delete IP from ruleset IPs list and ipset
		if _, ok := ipsetList.ips[r.ip]; ok {
			ip := net.ParseIP(r.ip)
			if ip == nil {
				return fmt.Errorf("parse IP %s", r.ip)
			}
			if err := m.delFromIPSet(r.ipsetName, ip); err != nil {
				return fmt.Errorf("delete ip from ipset: %w", err)
			}
			delete(ipsetList.ips, r.ip)
		}

		// if after delete, set still contains other IPs,
		// no need to delete firewall rule and we should exit here
		if len(ipsetList.ips) != 0 {
			return nil
		}

		// we delete last IP from the set, that means we need to delete
		// set itself and associated firewall rule too
		m.ipsetStore.deleteIpset(r.ipsetName)
		shouldDestroyIpset = true
	}

	if err := m.iptablesClient.Delete(tableName, r.chain, r.specs...); err != nil {
		return fmt.Errorf("failed to delete rule: %s, %v: %w", r.chain, r.specs, err)

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Treat ErrNotExist as success (element already gone) and continue the delete flow, since the desired end state is achieved.
  2. Recreate/synchronize the set when the whole set is missing before retrying the DEL.
  3. Run with CAP_NET_ADMIN and validate the IP family against the manager's v6 flag.
  4. Avoid external mutation of NETBIRD ipsets; use the manager's Reset to reconcile.

Example fix

// before
if err := m.delFromIPSet(r.ipsetName, ip); err != nil {
    return fmt.Errorf("delete ip from ipset: %w", err)
}

// after
if err := m.delFromIPSet(r.ipsetName, ip); err != nil {
    if errors.Is(err, ipset.ErrElementNotExist) || errors.Is(err, ipset.ErrSetNotExist) {
        log.Debugf("ip %s already absent from ipset %s", r.ip, r.ipsetName)
    } else {
        return fmt.Errorf("delete ip from ipset: %w", err)
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the element is really in the kernel set before issuing the DEL
if err := ipset.Test(name, &ipset.Entry{IP: ip, CIDR: cidr}); err != nil {
    if errors.Is(err, ipset.ErrElementNotExist) || errors.Is(err, ipset.ErrSetNotExist) {
        // already gone: skip the kernel DEL, still update local maps
    }
}

Try / catch

if err := mgr.DeletePeerRule(rule); err != nil {
    if strings.Contains(err.Error(), "delete ip from ipset") &&
        (errors.Is(err, ipset.ErrElementNotExist) || errors.Is(err, ipset.ErrSetNotExist)) {
        // element/set already absent: desired state achieved, ignore
        err = nil
    }
}

Prevention

When it happens

Trigger: DeletePeerRule after an external `ipset flush`/`destroy` removed the element or set while the manager's map still lists it; agent restart that repopulated ipsetStore from state but the kernel was reset; unprivileged execution; v4 IP passed to a v6 set's delete path.

Common situations: Hosts where operators periodically flush ipsets; crash/restart sequences that leave the memory map ahead of the kernel; cleanup code running after the network namespace changed.

Related errors


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