slackhq/nebula · error
failed to make read call for tun: %w
Error message
failed to make read call for tun: %w
What it means
Read() uses the RawConn's Read method to run the syscall read; if that control function reports an error (other than the recognized closed-file conditions), it is wrapped as 'failed to make read call for tun'. This indicates the outer read invocation itself failed (e.g. EBADF, EINTR patterns surfaced here, or Go's poll error text).
Source
Thrown at overlay/tun_netbsd.go:173
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
})
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 useView on GitHub (pinned to dd8f660c0a)
Solutions
- Inspect the wrapped errno (%w) to identify the cause; handle EINTR by retrying
- Ensure Close() happens after all readers exit
- Treat os.ErrClosed (returned for EBADF/closed-file) as expected during shutdown and re-open/re-create the tun if persistent
Example fix
// before
n, err := tun.Read(buf)
if err != nil { return err }
// after
n, err := tun.Read(buf)
if errors.Is(err, os.ErrClosed) { return nil } // expected shutdown
if err != nil { return err } Defensive patterns
Strategy: retry
Try / catch
n, err := tun.Read(buf)
if err != nil {
if errors.Is(err, os.ErrClosed) { return nil }
var errno syscall.Errno
if errors.As(err, &errno) && errors.Is(errno, syscall.EINTR) { continue }
return fmt.Errorf("tun read: %w", err)
} Prevention
- Map EINTR to retry, os.ErrClosed to graceful shutdown
- Inspect the wrapped errno before deciding to abort
- Keep readers alive for the lifetime of the fd
When it happens
Trigger: The tun fd is invalid at read time (after close/finalizer), the RawConn read fn returns a non-nil error that is not EBADF or 'use of closed file', or poll errors other than file-closing.
Common situations: Read after Close (the specific EBADF/closed cases are translated to os.ErrClosed, so this usually means a different errno like EINTR cascades or an unexpected descriptor state).
Related errors
- failed to make inner 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/d184ef7aea2ad39c.
Report an issue: GitHub.