{"record":{"id":"0b0234d0de1f1a7c","repo":"netbirdio/netbird","slug":"failed-to-remove-wireguard-interface-s-w","errorCode":null,"errorMessage":"failed to remove WireGuard interface %s: %w","messagePattern":"failed to remove WireGuard interface (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/iface/iface.go","lineNumber":240,"sourceCode":"\t// wireguard-go device.Close() waits for its send/receive goroutines\n\t// to drain. Some of those goroutines re-enter WGIface methods that\n\t// take w.mu (e.g. the packet filter DNS hook calls GetDevice()), so\n\t// holding the mutex here would deadlock the shutdown path.\n\ttun := w.tun\n\tw.mu.Unlock()\n\n\tif err := tun.Close(); err != nil {\n\t\tresult = multierror.Append(result, fmt.Errorf(\"failed to close wireguard interface %s: %w\", w.Name(), err))\n\t}\n\n\tif nbnetstack.IsEnabled() {\n\t\treturn errors.FormatErrorOrNil(result)\n\t}\n\n\tif err := w.waitUntilRemoved(); err != nil {\n\t\tlog.Warnf(\"failed to remove WireGuard interface %s: %v\", w.Name(), err)\n\t\tif err := w.Destroy(); err != nil {\n\t\t\tresult = multierror.Append(result, fmt.Errorf(\"failed to remove WireGuard interface %s: %w\", w.Name(), err))\n\t\t\treturn errors.FormatErrorOrNil(result)\n\t\t}\n\t\tlog.Infof(\"interface %s successfully removed\", w.Name())\n\t}\n\n\treturn errors.FormatErrorOrNil(result)\n}\n\n// SetFilter sets packet filters for the userspace implementation\nfunc (w *WGIface) SetFilter(filter device.PacketFilter) error {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\tif w.tun.FilteredDevice() == nil {\n\t\treturn fmt.Errorf(\"userspace packet filtering not handled on this device\")\n\t}\n\n\tw.filter = filter","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/iface/iface.go#L222-L258","documentation":"Emitted at the end of WGIface.Close() when waitUntilRemoved() timed out (interface still listed after 5 seconds of 100ms polling) and the forced w.Destroy() that follows also failed. It is a compounded teardown failure: neither graceful removal nor the platform-specific destroy cleared the interface, so it outlives the agent process.","triggerScenarios":"Kernel-mode interface lingering because routes, addresses, or another process still reference it, combined with Destroy() failing for its own per-OS reason (ifconfig destroy / netlink LinkDel / netsh); only reached when netstack is disabled.","commonSituations":"Rapid up/down cycles leaving zombie interfaces; external network or firewall daemons holding references; degraded environments where the destroy command cannot execute.","solutions":["Identify the holder: `ip link show <name>` / `ifconfig <name>` / `netsh interface show interface`, plus routes referencing it","Remove manually: `sudo ip link del <name>` (Linux), `sudo ifconfig <name> destroy` (BSD), adapter removal in Device Manager (Windows)","Fix the underlying Destroy() failure using its own error message (wrapped in this chain)","Reboot the host if the kernel refuses all removal"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"if _, err := net.InterfaceByName(name); err == nil {\n    // still present after Close: fall back to platform-specific destroy\n    if err := destroyInterface(name); err != nil {\n        log.Warnf(\"manual cleanup required for %s: %v\", name, err)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always run netbird down before stopping or rebuilding the daemon","Never let two agents manage the same interface name","Clean zombie interfaces before the next netbird up"],"tags":["go","netbird","interface","teardown","wireguard"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}