slackhq/nebula · error

failed to enable offload on multiqueue tun fd: %w

Error message

failed to enable offload on multiqueue tun fd: %w

What it means

This error is returned by tun.addQueue when creating a multiqueue tun device: the TUNSETOFFLOAD ioctl that enables checksum/segmentation offload on a newly opened queue file descriptor failed. The original fd is closed and the wrapped kernel error is included via %w, so the underlying cause (e.g. EINVAL, ENOTSUP) is always embedded. It only fires when vnetHdr is true, i.e. when offload/vnet-header support was negotiated.

Source

Thrown at overlay/tun_linux.go:353

	fd, err := unix.Open("/dev/net/tun", os.O_RDWR, 0)
	if err != nil {
		return err
	}

	flags := uint16(unix.IFF_TUN | unix.IFF_NO_PI | unix.IFF_MULTI_QUEUE)
	if t.vnetHdr {
		flags |= unix.IFF_VNET_HDR
	}
	if _, err = tunSetIff(fd, t.Device, flags); err != nil {
		_ = unix.Close(fd)
		return err
	}

	if t.vnetHdr {
		if err = ioctl(uintptr(fd), unix.TUNSETOFFLOAD, uintptr(t.offloadFlags)); err != nil {
			_ = unix.Close(fd)
			return fmt.Errorf("failed to enable offload on multiqueue tun fd: %w", err)
		}
	}

	err = t.readers.Add(fd)
	if err != nil {
		_ = unix.Close(fd)
		return err
	}

	return nil
}

func (t *tun) RoutesFor(ip netip.Addr) routing.Gateways {
	r, _ := t.routeTree.Load().Lookup(ip)
	return r
}

func (t *tun) deviceBytes() (o [16]byte) {

View on GitHub (pinned to dd8f660c0a)

Solutions

  1. Inspect the wrapped error (%w) with errors.Unwrap to identify the errno; EINVAL usually means unsupported offload flags.
  2. Update the kernel or run on a host whose tun driver supports the requested offload features.
  3. Run nebula with offload disabled (e.g. older tun config path or a build/config that leaves vnetHdr off) if the platform can't support it.
  4. Check that the process has permission to attach to the tun device (CAP_NET_ADMIN) and that the fd is valid.

Example fix

// before: opaque failure on kernels without UFO support
offloadFlags := unix.TUNSETOFFLOAD // all features requested
// after: retry with progressively fewer features or log the unwrapped cause
if err := ioctl(uintptr(fd), unix.TUNSETOFFLOAD, uintptr(t.offloadFlags)); err != nil {
    t.l.Warn("offload unsupported, continuing without", "err", err)
}
Defensive patterns

Strategy: fallback

Validate before calling

// check kernel offload support before enabling vnet hdr
flags := unix.TUN_F_CSUM | unix.TUN_F_TSO4 | unix.TUN_F_TSO6
if err := ioctl(uintptr(fd), unix.TUNSETOFFLOAD, uintptr(flags)); err != nil {
    log.Warn("offload unsupported, falling back to no-offload", "err", err)
}

Try / catch

if err := startOverlay(); err != nil {
    if strings.Contains(err.Error(), "failed to enable offload on multiqueue tun fd") {
        // unwrap and retry without offload
        log.Warn("retrying without offload", "cause", errors.Unwrap(err))
    } else { return err }
}

Prevention

When it happens

Trigger: Calling Queues() (which calls addQueue) on a Linux tun with vnetHdr enabled when the kernel rejects the TUNSETOFFLOAD flags — e.g. offloadFlags contains features the kernel/driver doesn't support, the fd is not a valid tun attach fd, or the device was already detached.

Common situations: Running on older kernels or virtualized NICs (some cloud hypervisors) that don't support all requested offload features like TSO/UFO; containers with restricted device access; mismatched nebula build vs kernel capabilities.

Related errors


AI-assisted analysis of slackhq/nebula@dd8f660c0a (2026-09-03). Data as JSON: /api/errors/a4b73045544c15e3. Report an issue: GitHub.