{"record":{"id":"99207916c33e1295","repo":"slackhq/nebula","slug":"newtunfromfd-not-supported-in-darwin","errorCode":null,"errorMessage":"newTunFromFd not supported in Darwin","messagePattern":"newTunFromFd not supported in Darwin","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"overlay/tun_darwin.go","lineNumber":154,"sourceCode":"\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) deviceBytes() (o [16]byte) {\n\tfor i, c := range t.Device {\n\t\to[i] = byte(c)\n\t}\n\treturn\n}\n\nfunc newTunFromFd(_ *config.C, _ *slog.Logger, _ int, _ []netip.Prefix) (*tun, error) {\n\treturn nil, fmt.Errorf(\"newTunFromFd not supported in Darwin\")\n}\n\nfunc (t *tun) Close() error {\n\tif t.f != nil {\n\t\treturn t.f.Close()\n\t}\n\treturn nil\n}\n\nfunc (t *tun) Activate() error {\n\tdevName := t.deviceBytes()\n\n\ts, err := unix.Socket(\n\t\tunix.AF_INET,\n\t\tunix.SOCK_DGRAM,\n\t\tunix.IPPROTO_IP,\n\t)\n\tif err != nil {","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/overlay/tun_darwin.go#L136-L172","documentation":"On Darwin, nebula cannot adopt an externally supplied TUN file descriptor; the utun control socket must be created by nebula itself via newTun. newTunFromFd is therefore a stub returning this error. Any attempt to construct the TUN from a caller-provided fd on macOS fails unconditionally.","triggerScenarios":"Calling tun.NewFromFd (newTunFromFd) on a darwin build — e.g. porting the Android-style fd handoff code path to macOS.","commonSituations":"Sharing embedding code between Android and macOS that passes VpnService-established fds; building an iOS/macOS Catalyst app that reuses the Android fd plumbing; tests invoking NewFromFd on darwin.","solutions":["On darwin, call tun.New (newTun) and let nebula create its own utun device instead of NewFromFd.","Branch on runtime.GOOS: use NewFromFd only for android, New for darwin/linux/bsd.","If the fd must come from outside the process, route through a NetworkExtension or restructure so nebula owns TUN creation."],"exampleFix":"// before\ntunDev, err := tun.NewFromFd(c, l, fd, vpnNetworks) // fails on darwin\n// after\nif runtime.GOOS == \"darwin\" {\n    tunDev, err = tun.New(c, l, vpnNetworks, false)\n} else {\n    tunDev, err = tun.NewFromFd(c, l, fd, vpnNetworks)\n}","handlingStrategy":"fallback","validationCode":"if runtime.GOOS == \"darwin\" && useProvidedFd {\n    return errors.New(\"darwin cannot adopt external TUN fds; use tun.New\")\n}","typeGuard":null,"tryCatchPattern":"t, err := tun.NewFromFd(c, l, fd, vpnNetworks)\nif err != nil && strings.Contains(err.Error(), \"not supported in Darwin\") {\n    t, err = tun.New(c, l, vpnNetworks, false) // nebula creates its own utun\n}","preventionTips":["Only call NewFromFd on android; branch on runtime.GOOS","On darwin always let nebula create the utun itself","Do not port the Android fd handoff pattern to macOS embeds"],"tags":["macos","darwin","tun-device","unsupported-platform","file-descriptor"],"backgroundTag":"tun-device-unsupported-platform","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"}