{"record":{"id":"4e5b401d5c9371f7","repo":"slackhq/nebula","slug":"error-closing-tun-file-w","errorCode":null,"errorMessage":"error closing tun file: %w","messagePattern":"error closing tun file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"overlay/tun_netbsd.go","lineNumber":125,"sourceCode":"\terr = t.reload(c, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tc.RegisterReloadCallback(func(c *config.C) {\n\t\terr := t.reload(c, false)\n\t\tif err != nil {\n\t\t\tutil.LogWithContextIfNeeded(\"failed to reload tun device\", err, t.l)\n\t\t}\n\t})\n\n\treturn t, nil\n}\n\nfunc (t *tun) Close() error {\n\tif t.f != nil {\n\t\tif err := t.f.Close(); err != nil {\n\t\t\treturn fmt.Errorf(\"error closing tun file: %w\", err)\n\t\t}\n\n\t\t// t.f.Close should have handled it for us but let's be extra sure\n\t\t_ = unix.Close(t.fd)\n\n\t\ts, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_IP)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer syscall.Close(s)\n\n\t\tifr := ifreq{Name: t.deviceBytes()}\n\t\terr = ioctl(uintptr(s), syscall.SIOCIFDESTROY, uintptr(unsafe.Pointer(&ifr)))\n\t\treturn err\n\t}\n\treturn nil\n}\n","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_netbsd.go#L107-L143","documentation":"tun.Close() wraps any error returned by closing the underlying os.File wrapping the /dev/tunN descriptor. It means the OS refused or failed the close, typically because the descriptor is already invalid/closed or was interrupted, so cleanup of the TUN device could not complete cleanly.","triggerScenarios":"Calling Close() on a tun whose t.f was already closed elsewhere (double Close), or a race where another goroutine closed the file, or an I/O error during close on the device.","commonSituations":"Application shutdown code calling Close() twice; concurrent readers/writers closing the tun; descriptors inherited or clobbered by exec.","solutions":["Ensure Close() is called only once per tun (use sync.Once or a closed flag)","Check that no other code path closes the same *os.File concurrently","Log the wrapped %w error to identify the underlying errno (e.g. EBADF)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"var closeOnce sync.Once\nfunc safeClose(t *tun) error {\n    var err error\n    closeOnce.Do(func() { err = t.Close() })\n    return err\n}","typeGuard":null,"tryCatchPattern":"if err := tun.Close(); err != nil {\n    var se *os.SyscallError\n    if errors.As(err, &se) && errors.Is(se.Err, os.ErrClosed) { return nil }\n    log.Printf(\"tun close: %v\", err)\n}","preventionTips":["Close the tun exactly once (sync.Once)","Never close the underlying file from other goroutines","Sequence shutdown: stop readers, then Close"],"tags":["netbsd","tun","close","file-descriptor"],"backgroundTag":"tun-close-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"}