netbirdio/netbird · error
error removing peer %s from interface %s: %w
Error message
error removing peer %s from interface %s: %w
What it means
The first half of RemoveEndpointAddress (remove the peer entirely so it can be re-added without an endpoint) failed at the wgctrl configure call. Because the strategy is delete-then-re-add, any error here is safe: the peer is still on the device with its old endpoint, so connectivity state is unchanged and the operation can simply be retried. The underlying causes are the usual kernel-configure ones: device vanished, permissions, or netlink transport failure.
Source
Thrown at client/iface/configurer/kernel_unix.go:107
func (c *KernelConfigurer) RemoveEndpointAddress(peerKey string) error {
peerKeyParsed, err := wgtypes.ParseKey(peerKey)
if err != nil {
return err
}
// Get the existing peer to preserve its allowed IPs
existingPeer, err := c.getPeer(c.deviceName, peerKey)
if err != nil {
return fmt.Errorf("get peer: %w", err)
}
removePeerCfg := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
Remove: true,
}
if err := c.configure(wgtypes.Config{Peers: []wgtypes.PeerConfig{removePeerCfg}}); err != nil {
return fmt.Errorf(`error removing peer %s from interface %s: %w`, peerKey, c.deviceName, err)
}
//Re-add the peer without the endpoint but same AllowedIPs
reAddPeerCfg := wgtypes.PeerConfig{
PublicKey: peerKeyParsed,
AllowedIPs: existingPeer.AllowedIPs,
ReplaceAllowedIPs: true,
}
if err := c.configure(wgtypes.Config{Peers: []wgtypes.PeerConfig{reAddPeerCfg}}); err != nil {
return fmt.Errorf(
`error re-adding peer %s to interface %s with allowed IPs %v: %w`,
peerKey, c.deviceName, existingPeer.AllowedIPs, err,
)
}
return nil
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Retry the whole RemoveEndpointAddress operation: no state was mutated, so the retry is safe
- Confirm the device still exists before retrying; recreate the interface if it was torn down intentionally
- Serialize peer mutations with a per-device lock to avoid netlink contention
- If it persists, capture wg show output to compare the device state against the configurer's view
Defensive patterns
Strategy: retry
Validate before calling
if _, err := net.InterfaceByName(c.deviceName); err != nil {
return fmt.Errorf("device gone; skip removal: %w", err)
} Type guard
func devicePresent(name string) bool {
_, err := net.InterfaceByName(name)
return err == nil
} Try / catch
err := configurer.RemoveEndpointAddress(peerKey)
for attempt := 0; attempt < 2 && err != nil; attempt++ {
if errors.Is(err, syscall.ENOENT) || errors.Is(err, syscall.EACCES) {
break // permanent: surface it
}
time.Sleep(50 * time.Millisecond)
err = configurer.RemoveEndpointAddress(peerKey) // remove step mutated nothing; safe to retry
} Prevention
- Remember the remove half of the operation is state-preserving: retry is always safe
- Serialize peer mutations per device to avoid netlink contention errors
- Surface wrapped syscall causes in logs to separate races from permission problems
When it happens
Trigger: The WireGuard interface was removed between getPeer and the remove configure; losing CAP_NET_ADMIN mid-operation; netlink buffer exhaustion under heavy concurrent peer churn.
Common situations: Interface lifecycle races during engine restart; parallel peer updates from a fast-changing network map; agents on hosts where uapi sockets are intermittently unavailable (containerized or seccomp-restricted environments).
Related errors
- received error "%w" while updating peer on interface %s with
- get peer: %w
- error re-adding peer %s to interface %s with allowed IPs %v:
- received error "%w" while removing peer %s from interface %s
- received error "%w" while configuring interface %s with port
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/5315d3c68541716c.
Report an issue: GitHub.