netbirdio/netbird · error

either peer or peer_groups must be provided

Error message

either peer or peer_groups must be provided

What it means

Returned by NetworkRouter.Validate (management/server/networks/routers/types/router.go:80): a network router has no destination peer at all. The peer-routing model requires the traffic to be terminated by either one peer or the members of at least one access group, so a router with neither is rejected before persistence.

Source

Thrown at management/server/networks/routers/types/router.go:80

		Masquerade: masquerade,
		Metric:     metric,
		Enabled:    enabled,
	}

	if err := r.Validate(); err != nil {
		return nil, err
	}

	return r, nil
}

func (n *NetworkRouter) Validate() error {
	if n.Peer != "" && len(n.PeerGroups) > 0 {
		return errors.New("peer and peer_groups cannot be set at the same time")
	}

	if n.Peer == "" && len(n.PeerGroups) == 0 {
		return errors.New("either peer or peer_groups must be provided")
	}

	return nil
}

func (n *NetworkRouter) ToAPIResponse() *api.NetworkRouter {
	return &api.NetworkRouter{
		Id:         n.ID,
		Peer:       &n.Peer,
		PeerGroups: &n.PeerGroups,
		Masquerade: n.Masquerade,
		Metric:     n.Metric,
		Enabled:    n.Enabled,
	}
}

func (n *NetworkRouter) FromAPIRequest(req *api.NetworkRouterRequest) {
	if req.Peer != nil {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Add a routing peer ID in the peer field, or at least one valid group ID in peer_groups
  2. Create the peer or the group first if it does not exist yet, then create the router

Example fix

// before
{ "network_id": "nwr-1", "enabled": true }  // no peer, no peer_groups

// after
{ "network_id": "nwr-1", "peer": "p-1", "enabled": true }
Defensive patterns

Strategy: validation

Validate before calling

func hasRoutingPeer(peer string, groups []string) bool {
    return peer != "" || len(groups) > 0
}

if !hasRoutingPeer(payload.Peer, payload.PeerGroups) {
    return errors.New("provide a routing peer or at least one peer group")
}

Try / catch

if err := payload.Validate(); err != nil { // client-side mirror of server Validate()
    return err
}
_, err := createRouter(ctx, networkID, payload)
if err != nil && strings.Contains(err.Error(), "either peer or peer_groups") {
    // prompt for a peer/group selection instead of retrying blindly
}

Prevention

When it happens

Trigger: POST /api/networks/{networkId}/routers (or update) where peer is empty/omitted and peer_groups is empty or omitted.

Common situations: Creating a router shell first and intending to attach a peer later (not supported); JSON marshalling that drops empty fields; frontend sending an empty array for peer_groups and empty string for peer.

Related errors


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