slackhq/nebula · error
newTunFromFd not supported in Darwin
Error message
newTunFromFd not supported in Darwin
What it means
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.
Source
Thrown at overlay/tun_darwin.go:154
c.RegisterReloadCallback(func(c *config.C) {
err := t.reload(c, false)
if err != nil {
util.LogWithContextIfNeeded("failed to reload tun device", err, t.l)
}
})
return t, nil
}
func (t *tun) deviceBytes() (o [16]byte) {
for i, c := range t.Device {
o[i] = byte(c)
}
return
}
func newTunFromFd(_ *config.C, _ *slog.Logger, _ int, _ []netip.Prefix) (*tun, error) {
return nil, fmt.Errorf("newTunFromFd not supported in Darwin")
}
func (t *tun) Close() error {
if t.f != nil {
return t.f.Close()
}
return nil
}
func (t *tun) Activate() error {
devName := t.deviceBytes()
s, err := unix.Socket(
unix.AF_INET,
unix.SOCK_DGRAM,
unix.IPPROTO_IP,
)
if err != nil {View on GitHub (pinned to dd8f660c0a)
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.
Example fix
// before
tunDev, err := tun.NewFromFd(c, l, fd, vpnNetworks) // fails on darwin
// after
if runtime.GOOS == "darwin" {
tunDev, err = tun.New(c, l, vpnNetworks, false)
} else {
tunDev, err = tun.NewFromFd(c, l, fd, vpnNetworks)
} Defensive patterns
Strategy: fallback
Validate before calling
if runtime.GOOS == "darwin" && useProvidedFd {
return errors.New("darwin cannot adopt external TUN fds; use tun.New")
} Try / catch
t, err := tun.NewFromFd(c, l, fd, vpnNetworks)
if err != nil && strings.Contains(err.Error(), "not supported in Darwin") {
t, err = tun.New(c, l, vpnNetworks, false) // nebula creates its own utun
} Prevention
- 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
When it happens
Trigger: Calling tun.NewFromFd (newTunFromFd) on a darwin build — e.g. porting the Android-style fd handoff code path to macOS.
Common situations: 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.
Related errors
AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03).
Data as JSON: /api/errors/99207916c33e1295.
Report an issue: GitHub.