netbirdio/netbird · error

create nftables manager: %w

Error message

create nftables manager: %w

What it means

Raised in ShutdownState.Cleanup (client/firewall/nftables/state_linux.go:39) during crash-recovery: the agent persisted its nftables state before a shutdown, and on the next start it recreates a full nftables manager from that InterfaceState just to Close it (which flushes the leftover netbird rules/table). Create performs the whole table/chain setup over netlink and fails without CAP_NET_ADMIN, without kernel nftables support, or on any netlink handshake error.

Source

Thrown at client/firewall/nftables/state_linux.go:39

	return i.WGAddress
}

type ShutdownState struct {
	InterfaceState *InterfaceState `json:"interface_state,omitempty"`
}

func (s *ShutdownState) Name() string {
	return "nftables_state"
}

func (s *ShutdownState) Cleanup() error {
	mtu := s.InterfaceState.MTU
	if mtu == 0 {
		mtu = iface.DefaultMTU
	}
	nft, err := Create(s.InterfaceState, mtu)
	if err != nil {
		return fmt.Errorf("create nftables manager: %w", err)
	}

	if err := nft.Close(nil); err != nil {
		return fmt.Errorf("reset nftables manager: %w", err)
	}

	return nil
}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Run the recovery with the same privileges the agent had when it wrote the state (root or CAP_NET_ADMIN)
  2. Verify nftables availability first: 'nft list ruleset' as the same user; load nf_tables module if missing
  3. If the environment intentionally lost nftables support, clear the persisted shutdown state file so cleanup is skipped on subsequent starts
  4. Log and continue (the leftover rules die with the old table lifetime / reboot) rather than blocking agent startup on cleanup failure

Example fix

// before
nft, err := Create(s.InterfaceState, mtu)
if err != nil {
    return fmt.Errorf("create nftables manager: %w", err)
}
// after - skip cleanup when nftables is unavailable in the current environment
if !nftablesAvailable() {
    log.Warn("nftables unavailable, skipping shutdown-state cleanup")
    return nil
}
nft, err := Create(s.InterfaceState, mtu)
if err != nil {
    return fmt.Errorf("create nftables manager: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

// before attempting crash-recovery cleanup
if os.Geteuid() != 0 && !hasCapNetAdmin() {
    log.Warn("skipping nftables shutdown-state cleanup: insufficient privileges")
    return nil
}

Type guard

func nftablesAvailable() bool {
    f, err := os.Open("/proc/net/netfilter/nfnetlink_queue")
    if err == nil {
        f.Close()
    }
    c, err := net.Dial("unix", "") // probe replaced by exec probe below
    _ = c
    return exec.Command("nft", "list", "ruleset").Run() == nil
}

Try / catch

if err := shutdownState.Cleanup(); err != nil {
    if isPermissionErr(err) || isNotExistErr(err) {
        log.Warnf("nftables cleanup skipped: %v", err)
        return // do not block startup on cleanup
    }
    return err
}

Prevention

When it happens

Trigger: Agent restarted after a crash/kill without root privileges; recovery running inside a container or VM where nftables was present at write time but absent at restore time (kernel/module change, bind-mount restrictions); netlink socket creation blocked by seccomp.

Common situations: Host downgraded or kernel changed between runs; systemd unit lost AmbientCapabilities; agent running in privileged container where nftables module is not loaded; 'netbird down' invoked by an unprivileged helper triggering state restore.

Related errors


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