netbirdio/netbird · error

invalid rule type

Error message

invalid rule type

What it means

DeletePeerRule performs a checked type assertion of the firewall.Rule interface to the iptables package's concrete *Rule. Passing anything else, a rule from the nftables manager, a mock, or a typed-nil *Rule wrapped in the interface, fails the assertion and returns this error. It is a programming/API-contract error, not an environment problem: rules are only meaningful to the manager implementation that created them.

Source

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

		ruleID:      uuid.New().String(),
		specs:       specs,
		mangleSpecs: mangleSpecs,
		ipsetName:   ipsetName,
		ip:          ip.String(),
		chain:       chain,
		v6:          m.v6,
	}

	m.updateState()

	return []firewall.Rule{rule}, nil
}

// 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

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Only pass rules returned by the same iptables Manager instance's AddPeerFiltering/AddRouteFiltering.
  2. Keep rules stored as their concrete type or re-fetch them from the manager instead of crossing implementations.
  3. In tests, construct rules via the real manager or export a constructor for fixtures.
  4. Add a type switch/guard at the call site to fail fast with context.

Example fix

// before
err := mgr.DeletePeerRule(rule) // rule came from another backend

// after
itr, ok := rule.(*iptables.Rule)
if !ok {
    return fmt.Errorf("rule %T not created by the iptables manager", rule)
}
err := mgr.DeletePeerRule(itr)
Defensive patterns

Strategy: type-guard

Validate before calling

if _, ok := rule.(*iptables.Rule); !ok {
    return fmt.Errorf("refusing to delete rule of type %T through the iptables manager", rule)
}

Type guard

func isIPTablesRule(r firewall.Rule) (*iptables.Rule, bool) {
    ir, ok := r.(*iptables.Rule)
    return ir, ok
}

Try / catch

if err := mgr.DeletePeerRule(rule); err != nil {
    if strings.Contains(err.Error(), "invalid rule type") {
        // programmer error: rule came from another backend; drop it from tracking
        log.Errorf("dropping foreign rule %T", rule)
    }
}

Prevention

When it happens

Trigger: Forwarding a firewall.Rule obtained from a different backend (nftables/pf/WFP) into the iptables manager's DeletePeerRule; unit tests passing mocks or plain structs; storing rules as firewall.Rule across a manager recreation and deleting them from the new instance; deleting a nil *Rule via the interface.

Common situations: Firewall-manager fallback code that swaps implementations at runtime but reuses cached rules; test code exercising DeletePeerRule with hand-built rule values.

Related errors


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