netbirdio/netbird · error

remove endpoint address: %w

Error message

remove endpoint address: %w

What it means

The second phase of WGUSPConfigurer.RemoveEndpointAddress failed: after removing the peer, re-adding it with its original allowed IPs and no endpoint via IpcSet returned an error. The two-phase remove/re-add is not atomic, so a failure here leaves the peer absent from the device. Causes include the device closing between the two IpcSet calls or an allowed_ip line the UAPI parser rejects.

Source

Thrown at client/iface/configurer/usp.go:187

		Peers: []wgtypes.PeerConfig{peer},
	}
	if ipcErr := c.device.IpcSet(toWgUserspaceString(config)); ipcErr != nil {
		return fmt.Errorf("failed to remove peer: %s", ipcErr)
	}

	// Build the peer config
	peer = wgtypes.PeerConfig{
		PublicKey:         peerKeyParsed,
		ReplaceAllowedIPs: true,
		AllowedIPs:        allowedIPs,
	}

	config = wgtypes.Config{
		Peers: []wgtypes.PeerConfig{peer},
	}

	if err := c.device.IpcSet(toWgUserspaceString(config)); err != nil {
		return fmt.Errorf("remove endpoint address: %w", err)
	}

	return nil
}

func (c *WGUSPConfigurer) RemovePeer(peerKey string) error {
	peerKeyParsed, err := wgtypes.ParseKey(peerKey)
	if err != nil {
		return err
	}

	peer := wgtypes.PeerConfig{
		PublicKey: peerKeyParsed,
		Remove:    true,
	}

	config := wgtypes.Config{
		Peers: []wgtypes.PeerConfig{peer},

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. On failure, re-add the peer with the saved allowedIPs to restore state, or re-run RemoveEndpointAddress
  2. Serialize configurer mutations so IpcSet sequences cannot interleave
  3. Perform endpoint cleanup before device Close in the shutdown order
  4. Log the saved allowedIPs with the error so recovery input is available

Example fix

// before: fail and leave the peer deleted
if err := c.device.IpcSet(toWgUserspaceString(config)); err != nil {
	return fmt.Errorf("remove endpoint address: %w", err)
}

// after: attempt one recovery re-add, then report
if err := c.device.IpcSet(toWgUserspaceString(config)); err != nil {
	restore := wgtypes.Config{Peers: []wgtypes.PeerConfig{{PublicKey: peerKeyParsed, ReplaceAllowedIPs: true, AllowedIPs: allowedIPs}}}
	if rErr := c.device.IpcSet(toWgUserspaceString(restore)); rErr != nil {
		return fmt.Errorf("remove endpoint address: %w (restore failed: %v)", err, rErr)
	}
	return fmt.Errorf("remove endpoint address: %w", err)
}
Defensive patterns

Strategy: retry

Try / catch

err := uspCfg.RemoveEndpointAddress(peerKey)
if err != nil && strings.Contains(err.Error(), "remove endpoint address") {
	// second phase failed and the peer is now absent: recover by re-running,
	// or re-adding the peer with its saved allowed IPs
	retryErr := uspCfg.RemoveEndpointAddress(peerKey)
	if retryErr != nil && strings.Contains(retryErr.Error(), "not found") {
		retryErr = nil // peer gone: endpoint already cleared by side effect
	}
	err = retryErr
}
return err

Prevention

When it happens

Trigger: Device Closed between the removal and re-add IpcSet calls; an allowed IP preserved from the dump that the parser now rejects; concurrent IpcSet from another goroutine invalidating state mid-sequence.

Common situations: Shutdown racing endpoint cleanup, leaving the peer deleted; reconnect flow rebuilding the device during the operation; duplicated allowed IPs or malformed CIDRs in the original dump.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/5150e75301caae8e. Report an issue: GitHub.