netbirdio/netbird · critical
link add: %w
Error message
link add: %w
What it means
netlink.LinkAdd(l) failed with an error other than EEXIST (EEXIST is tolerated with 'interface already exists, will reuse'). The link reports Type() "wireguard", so RTM_NEWLINK requires the wireguard kernel module; an unsupported kind, missing privileges, or an invalid name are the classic causes. This is the kernel-mode interface creation step and is fatal to bring-up.
Source
Thrown at client/iface/device/wg_link_linux.go:72
default:
return fmt.Errorf("link by name: %w", err)
}
}
// remove if interface exists
if link != nil {
err = netlink.LinkDel(l)
if err != nil {
return err
}
}
log.Debugf("adding device: %s", name)
err = netlink.LinkAdd(l)
if os.IsExist(err) {
log.Infof("interface %s already exists. Will reuse.", name)
} else if err != nil {
return fmt.Errorf("link add: %w", err)
}
return nil
}
func (l *wgLink) setMTU(mtu int) error {
if err := netlink.LinkSetMTU(l, mtu); err != nil {
log.Errorf("error setting MTU on interface: %s", l.attrs.Name)
return fmt.Errorf("link set mtu: %w", err)
}
return nil
}
func (l *wgLink) up() error {
if err := netlink.LinkSetUp(l); err != nil {
log.Errorf("error bringing up interface: %s", l.attrs.Name)View on GitHub (pinned to 93e97f4bf1)
Solutions
- Load the module (modprobe wireguard) or install matching kernel modules and run depmod
- Run with root/CAP_NET_ADMIN
- Shorten the interface name to 15 bytes or fewer
- If the kernel cannot provide wireguard, force userspace mode with NB_WG_KERNEL_DISABLED=true or rely on the automatic userspace fallback
Example fix
// before
if err := l.recreate(); err != nil {
return err
}
// after
if !device.WireGuardModuleIsLoaded() {
return fmt.Errorf("kernel wireguard unavailable; run in userspace mode")
}
if err := l.recreate(); err != nil {
return err
} Defensive patterns
Strategy: validation
Validate before calling
if !device.WireGuardModuleIsLoaded() {
// no kernel wireguard: use userspace mode instead of attempting LinkAdd
return useUserspaceMode()
}
if len(ifaceName) > 15 {
return fmt.Errorf("interface name %q exceeds 15 bytes", ifaceName)
} Try / catch
if err := netlink.LinkAdd(l); err != nil && !os.IsExist(err) {
if errors.Is(err, syscall.EPERM) {
// missing CAP_NET_ADMIN
} else if errors.Is(err, syscall.EPROTONOSUPPORT) || errors.Is(err, syscall.EOPNOTSUPP) {
// wireguard kind unsupported: module missing
}
return fmt.Errorf("link add: %w", err)
} Prevention
- Probe WireGuardModuleIsLoaded() before choosing kernel mode
- Keep interface names within 15 bytes
- Set NB_WG_KERNEL_DISABLED=true on hosts that cannot load the wireguard module
When it happens
Trigger: wireguard module unavailable (unsupported kind error), EPERM without CAP_NET_ADMIN, EINVAL for a name over 15 bytes or invalid attributes, ENOMEM/ENFILE at the interface limit.
Common situations: Old or custom kernels without wireguard, stripped module sets in minimal images, overlong interface names, kernel/userspace mode selection picking kernel mode on an unsupported host.
Related errors
- create ipset: %w
- create ipset %s: %w
- add IP to ipset %s: %w
- add output DNAT rule: %w
- couldn't find module path for %s, error: %v
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/2636b4b91ca81617.
Report an issue: GitHub.