slackhq/nebula · error

failed to get tun flags

Error message

failed to get tun flags: %s

What it means

Right after setting the MTU, Activate reads the interface flags with the SIOCGIFFLAGS ioctl to learn the device state before configuring routes/addresses. If that ioctl fails, the errno is wrapped in this error. It usually indicates the interface does not exist or the caller lacks privilege to query it.

Solutions

  1. Confirm the interface exists: `ifconfig <devName>`; fix tun.dev if it does not.
  2. Run with root privileges so interface ioctls are allowed.
  3. Ensure no other VPN tool is deleting/recreating the utun while nebula activates; stop competing VPNs.
  4. Restart nebula so a fresh utun is created and activated atomically.

Example fix

// before (nebula.yml)
tun:
  dev: utun99   # never created / wrong name
// after
tun:
  dev: utun50   # or omit dev so nebula uses the name it just created
Defensive patterns

Strategy: validation

Validate before calling

out, err := exec.Command("ifconfig", devName).Output()
if err != nil || len(out) == 0 {
    return fmt.Errorf("interface %s does not exist before activate", devName)
}

Try / catch

if err := start(); err != nil && strings.Contains(err.Error(), "failed to get tun flags") {
    // interface missing or permission denied: check ifconfig and privileges
}

Prevention

When it happens

Trigger: ioctl(fd, SIOCGIFFLAGS, ifReq{devName}) fails during tun.Activate — devName not present in the interface table, fd not a valid control socket for that device, or permission denial.

Common situations: tun.dev configured with a name that was never created; interface destroyed between connect and activate (competing VPN); running without sufficient privileges in restricted macOS environments.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/33172a8327d8c449. Report an issue: GitHub.

Appendix: source

Thrown at overlay/tun_darwin.go:188

		unix.IPPROTO_IP,
	)
	if err != nil {
		return err
	}
	defer unix.Close(s)

	fd := uintptr(s)

	// Set the MTU on the device
	ifm := ifreqMTU{Name: devName, MTU: int32(t.DefaultMTU)}
	if err = ioctl(fd, unix.SIOCSIFMTU, uintptr(unsafe.Pointer(&ifm))); err != nil {
		return fmt.Errorf("failed to set tun mtu: %v", err)
	}

	// Get the device flags
	ifrf := ifReq{Name: devName}
	if err = ioctl(fd, unix.SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifrf))); err != nil {
		return fmt.Errorf("failed to get tun flags: %s", err)
	}

	linkAddr, err := getLinkAddr(t.Device)
	if err != nil {
		return err
	}
	if linkAddr == nil {
		return fmt.Errorf("unable to discover link_addr for tun interface")
	}
	t.linkAddr = linkAddr

	for _, network := range t.vpnNetworks {
		if network.Addr().Is4() {
			err = t.activate4(network)
			if err != nil {
				return err
			}
		} else {

View on GitHub (pinned to dd8f660c0a)