slackhq/nebula · error

failed to get syscall conn for tun: %w

Error message

failed to get syscall conn for tun: %w

What it means

Read() calls f.SyscallConn() to obtain a raw syscall.RawConn for the TUN file before issuing the blocking read. If Go's runtime cannot produce a syscall conn for the file descriptor, the read cannot proceed and this error wraps the cause.

Source

Thrown at overlay/tun_netbsd.go:147

		_ = unix.Close(t.fd)

		s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)
		if err != nil {
			return err
		}
		defer syscall.Close(s)

		ifr := ifreq{Name: t.deviceBytes()}
		err = ioctl(uintptr(s), syscall.SIOCIFDESTROY, uintptr(unsafe.Pointer(&ifr)))
		return err
	}
	return nil
}

func (t *tun) Read(to []byte) (int, error) {
	rc, err := t.f.SyscallConn()
	if err != nil {
		return 0, fmt.Errorf("failed to get syscall conn for tun: %w", err)
	}

	var errno syscall.Errno
	var n uintptr
	err = rc.Read(func(fd uintptr) bool {
		// first 4 bytes is protocol family, in network byte order
		head := [4]byte{}
		iovecs := []syscall.Iovec{
			{&head[0], 4},
			{&to[0], uint64(len(to))},
		}

		n, _, errno = syscall.Syscall(syscall.SYS_READV, fd, uintptr(unsafe.Pointer(&iovecs[0])), uintptr(2))
		if errno.Temporary() {
			// We got an EAGAIN, EINTR, or EWOULDBLOCK, go again
			return false
		}
		return true

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Stop the Read loop before calling Close() (signal via context/channel)
  2. Handle os.ErrClosed from Read as a normal shutdown condition, not a fault
  3. Verify the tun was created through newTun so t.f is a valid *os.File

Example fix

// before
go tun.Read(buf)
tun.Close()
// after
done := make(chan struct{})
go func() { defer close(done); tun.Read(buf) }()
tun.Close()
<-done
Defensive patterns

Strategy: try-catch

Validate before calling

// before reading
if tun == nil || tun.f == nil { return errors.New("tun not initialized") }

Try / catch

n, err := tun.Read(buf)
if err != nil {
    if errors.Is(err, os.ErrClosed) || strings.Contains(err.Error(), "syscall conn") {
        return // shutdown path
    }
    return fmt.Errorf("tun read: %w", err)
}

Prevention

When it happens

Trigger: t.f is nil or already closed when Read is invoked, or the *os.File wraps something that cannot expose a SyscallConn (unexpected file type).

Common situations: Read loop racing with Close() during shutdown so the file is already finalized; mis-constructed tun objects in tests.

Related errors


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