netbirdio/netbird · error

router not part of network

Error message

router not part of network

What it means

Returned by managerImpl.GetRouter (management/server/networks/routers/manager.go:151) after the router was successfully fetched by ID from the store: the loaded router exists in the account, but its NetworkID does not equal the networkID argument of the request. It is a referential-integrity guard so a router cannot be read or mutated through the REST path of a network it does not belong to.

Source

Thrown at management/server/networks/routers/manager.go:151

	return router, nil
}

func (m *managerImpl) GetRouter(ctx context.Context, accountID, userID, networkID, routerID string) (*types.NetworkRouter, error) {
	ok, ctx, err := m.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Networks, operations.Read)
	if err != nil {
		return nil, status.NewPermissionValidationError(err)
	}
	if !ok {
		return nil, status.NewPermissionDeniedError()
	}

	router, err := m.store.GetNetworkRouterByID(ctx, store.LockingStrengthNone, accountID, routerID)
	if err != nil {
		return nil, fmt.Errorf("failed to get network router: %w", err)
	}

	if router.NetworkID != networkID {
		return nil, errors.New("router not part of network")
	}

	return router, nil
}

func (m *managerImpl) UpdateRouter(ctx context.Context, userID string, router *types.NetworkRouter) (*types.NetworkRouter, error) {
	ok, ctx, err := m.permissionsManager.ValidateUserPermissions(ctx, router.AccountID, userID, modules.Networks, operations.Update)
	if err != nil {
		return nil, status.NewPermissionValidationError(err)
	}
	if !ok {
		return nil, status.NewPermissionDeniedError()
	}

	var network *networkTypes.Network
	var snap *affectedpeers.Snapshot
	var change affectedpeers.Change
	err = m.store.ExecuteInTransaction(ctx, func(transaction store.Store) error {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. List the routers of the network you are targeting (GET /api/networks/{networkId}/routers) and use the router ID that actually appears there
  2. If the router lives elsewhere, correct the networkId segment of the URL/path to the network the router really belongs to
  3. If the router must serve this network, delete it and create a new one under the correct network (a router's network assignment cannot be moved)

Example fix

// before: router ctsr-123 belongs to network 'b', not 'a'
GET /api/networks/a/routers/ctsr-123  // 400 router not part of network

// after: address the router through its actual network
GET /api/networks/b/routers/ctsr-123
Defensive patterns

Strategy: validation

Validate before calling

// Before calling GetRouter, confirm the router is in this network
routers, err := listNetworkRouters(ctx, accountID, networkID)
if err != nil { return err }
found := false
for _, r := range routers {
    if r.Id == routerID { found = true; break }
}
if !found {
    return fmt.Errorf("router %s is not part of network %s; pick one of the listed routers", routerID, networkID)
}

Try / catch

// The error is a plain errors.New (no sentinel); match on message or the API's 400 status
router, err := mgr.GetRouter(ctx, accountID, userID, networkID, routerID)
if err != nil {
    if strings.Contains(err.Error(), "router not part of network") {
        // re-enumerate routers of the network and correct the ID
    }
    return err
}

Prevention

When it happens

Trigger: Calling GET /api/networks/{networkId}/routers/{routerId} (or the corresponding gRPC/manager call) where routerID belongs to a different network in the same account. Typical after the router was deleted and recreated under another network while a dashboard, CLI or script still holds the old pairing of IDs.

Common situations: Stale UI state after a network resource was recreated; scripts that template the network ID but paste a router ID from another environment; renaming/rebuilding networks in IaC pipelines while reusing router IDs.

Related errors


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