slackhq/nebula · error

SYS_CONNECT

Error message

SYS_CONNECT: %v

What it means

newTun connects the control socket to the utun kernel control using SockaddrCtl with the resolved control ID and a unit number derived from the requested interface index (Unit = ifIndex + 1). If unix.Connect fails, this error wraps the errno. Commonly this means the requested utun unit number is already taken or invalid.

Solutions

  1. Remove tun.dev from config (or leave it unset) so macOS assigns the next available utun unit automatically.
  2. Check `ifconfig` for the utunN device and pick an unused unit if a fixed name is required.
  3. Kill any stale nebula/VPN processes holding the utun unit and retry.
  4. Use a lower unit number (macOS reserves utun0-utun3 for system services).

Example fix

// before (nebula.yml)
tun:
  dev: utun3
// after
tun:
  dev: utun50  # or omit 'dev' entirely to auto-assign
Defensive patterns

Strategy: validation

Validate before calling

// before starting nebula, if tun.dev is pinned, verify it is free:
out, _ := exec.Command("ifconfig", devName).Output()
if len(out) > 0 {
    return fmt.Errorf("interface %s already exists; unset tun.dev to auto-assign", devName)
}

Try / catch

if err := start(); err != nil && strings.Contains(err.Error(), "SYS_CONNECT") {
    // utun unit likely in use: clear tun.dev and retry with auto-assigned unit
}

Prevention

When it happens

Trigger: Explicitly configuring a tun device name (e.g. utun5) whose unit number is already in use, requesting a unit above the kernel maximum, or the control ID being stale after kernel state changes.

Common situations: Config tun.dev set to a specific utunN that macOS already allocated (e.g. utun3 used by VPN/icloud private relay); leftover utun from a crashed previous nebula instance.

Related errors


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

Appendix: source

Thrown at overlay/tun_darwin.go:110

	fd, err := unix.Socket(unix.AF_SYSTEM, unix.SOCK_DGRAM, unix.AF_SYS_CONTROL)
	if err != nil {
		return nil, fmt.Errorf("system socket: %v", err)
	}

	var ctlInfo = &unix.CtlInfo{}
	copy(ctlInfo.Name[:], utunControlName)

	err = unix.IoctlCtlInfo(fd, ctlInfo)
	if err != nil {
		return nil, fmt.Errorf("CTLIOCGINFO: %v", err)
	}

	err = unix.Connect(fd, &unix.SockaddrCtl{
		ID:   ctlInfo.Id,
		Unit: uint32(ifIndex) + 1,
	})
	if err != nil {
		return nil, fmt.Errorf("SYS_CONNECT: %v", err)
	}

	name, err = unix.GetsockoptString(fd, unix.AF_SYS_CONTROL, _UTUN_OPT_IFNAME)
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve tun name: %w", err)
	}

	err = unix.SetNonblock(fd, true)
	if err != nil {
		return nil, fmt.Errorf("SetNonblock: %v", err)
	}

	t := &tun{
		f:           os.NewFile(uintptr(fd), ""),
		Device:      name,
		vpnNetworks: vpnNetworks,
		DefaultMTU:  c.GetInt("tun.mtu", DefaultMTU),
		l:           l,

View on GitHub (pinned to dd8f660c0a)