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 trueView on GitHub (pinned to dd8f660c0a)
Solutions
- Stop the Read loop before calling Close() (signal via context/channel)
- Handle os.ErrClosed from Read as a normal shutdown condition, not a fault
- 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
- Stop all Read loops before Close()
- Use a done channel/context to coordinate reader goroutines
- Only construct tun via the library's newTun
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
- error closing tun file: %w
- failed to make read call for tun: %w
- failed to make inner read call for tun: %w
- newTunFromFd not supported in FreeBSD
- failed to set tun device as nonblocking: %w
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/3a95a0629f34ffa4.
Report an issue: GitHub.