slackhq/nebula · error
failed to make inner read call for tun: %w
Error message
failed to make inner read call for tun: %w
What it means
The inner read performed inside the RawConn control function returned a non-zero syscall.Errno; Read() wraps it as 'failed to make inner read call for tun'. This is the actual errno from reading the /dev/tunN descriptor, e.g. EIO or EAGAIN handling surfaced by the library.
Source
Thrown at overlay/tun_netbsd.go:177
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
})
if err != nil {
if err == syscall.EBADF || err.Error() == "use of closed file" {
// Go doesn't export poll.ErrFileClosing but happily reports it to us so here we are
// https://github.com/golang/go/blob/master/src/internal/poll/fd_poll_runtime.go#L121
return 0, os.ErrClosed
}
return 0, fmt.Errorf("failed to make read call for tun: %w", err)
}
if errno != 0 {
return 0, fmt.Errorf("failed to make inner read call for tun: %w", errno)
}
// fix bytes read number to exclude header
bytesRead := int(n)
if bytesRead < 0 {
return bytesRead, nil
} else if bytesRead < 4 {
return 0, nil
} else {
return bytesRead - 4, nil
}
}
// Write is only valid for single threaded use
func (t *tun) Write(from []byte) (int, error) {
if len(from) <= 1 {
return 0, syscall.EIO
}View on GitHub (pinned to dd8f660c0a)
Solutions
- Check the wrapped errno: EAGAIN/EWOULDBLOCK can be retried, EIO means recreate the device
- Recreate the TUN device if it was destroyed (ifconfig tunN create)
- Handle os.ErrClosed separately as graceful shutdown
Example fix
// before
if err != nil { log.Fatal(err) }
// after
var errno syscall.Errno
if errors.As(err, &errno) && errors.Is(errno, syscall.EAGAIN) { continue /* retry */ }
if err != nil { log.Fatal(err) } Defensive patterns
Strategy: retry
Try / catch
n, err := tun.Read(buf)
var errno syscall.Errno
if errors.As(err, &errno) {
switch {
case errors.Is(errno, syscall.EAGAIN), errors.Is(errno, syscall.EINTR):
continue // retry
case errors.Is(errno, syscall.EIO):
return recreateTun() // device gone
}
}
if err != nil { return err } Prevention
- Monitor the interface with ifconfig tunN before assuming code bugs
- Recreate the device on persistent EIO
- Differentiate transient (EAGAIN/EINTR) from fatal errnos
When it happens
Trigger: unix.Read on the tun fd inside rc.Read returns an errno such as EIO (device detached), EINTR, or EAGAIN that the code does not swallow, or a negative/failed read result.
Common situations: TUN device destroyed with ifconfig tunN destroy while reading; kernel driver errors; reading after the interface is down.
Related errors
- failed to make read call for tun: %w
- failed to get syscall conn for tun: %w
- failed to set tun device as nonblocking: %w
- newTunFromFd not supported in NetBSD
- a device name in the format of /dev/tunN must be specified
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/a2b2275e87ce467c.
Report an issue: GitHub.