{"record":{"id":"d184ef7aea2ad39c","repo":"slackhq/nebula","slug":"failed-to-make-read-call-for-tun-w","errorCode":null,"errorMessage":"failed to make read call for tun: %w","messagePattern":"failed to make read call for tun: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"overlay/tun_netbsd.go","lineNumber":173,"sourceCode":"\t\tiovecs := []syscall.Iovec{\n\t\t\t{&head[0], 4},\n\t\t\t{&to[0], uint64(len(to))},\n\t\t}\n\n\t\tn, _, errno = syscall.Syscall(syscall.SYS_READV, fd, uintptr(unsafe.Pointer(&iovecs[0])), uintptr(2))\n\t\tif errno.Temporary() {\n\t\t\t// We got an EAGAIN, EINTR, or EWOULDBLOCK, go again\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t})\n\tif err != nil {\n\t\tif err == syscall.EBADF || err.Error() == \"use of closed file\" {\n\t\t\t// Go doesn't export poll.ErrFileClosing but happily reports it to us so here we are\n\t\t\t// https://github.com/golang/go/blob/master/src/internal/poll/fd_poll_runtime.go#L121\n\t\t\treturn 0, os.ErrClosed\n\t\t}\n\t\treturn 0, fmt.Errorf(\"failed to make read call for tun: %w\", err)\n\t}\n\n\tif errno != 0 {\n\t\treturn 0, fmt.Errorf(\"failed to make inner read call for tun: %w\", errno)\n\t}\n\n\t// fix bytes read number to exclude header\n\tbytesRead := int(n)\n\tif bytesRead < 0 {\n\t\treturn bytesRead, nil\n\t} else if bytesRead < 4 {\n\t\treturn 0, nil\n\t} else {\n\t\treturn bytesRead - 4, nil\n\t}\n}\n\n// Write is only valid for single threaded use","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_netbsd.go#L155-L191","documentation":"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).","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"// before\nn, err := tun.Read(buf)\nif err != nil { return err }\n// after\nn, err := tun.Read(buf)\nif errors.Is(err, os.ErrClosed) { return nil } // expected shutdown\nif err != nil { return err }","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"n, err := tun.Read(buf)\nif err != nil {\n    if errors.Is(err, os.ErrClosed) { return nil }\n    var errno syscall.Errno\n    if errors.As(err, &errno) && errors.Is(errno, syscall.EINTR) { continue }\n    return fmt.Errorf(\"tun read: %w\", err)\n}","preventionTips":["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"],"tags":["netbsd","tun","read","syscall"],"backgroundTag":"tun-read-syscall-failed","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}