netbirdio/netbird · error

resource not part of network

Error message

resource not part of network

What it means

Returned by managerImpl.GetResource: the resource was fetched by ID successfully, but its resource.NetworkID does not equal the networkID argument of the request. The API route is scoped to /networks/{networkId}/resources/{resourceId}, so this fires when the two IDs belong to different networks. It surfaces as a client error, not a store error.

Source

Thrown at management/server/networks/resources/manager.go:200

	return eventsToStore, snap, nil
}

func (m *managerImpl) GetResource(ctx context.Context, accountID, userID, networkID, resourceID string) (*types.NetworkResource, 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()
	}

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

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

	return resource, nil
}

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

	resourceType, domain, prefix, err := types.GetResourceType(resource.Address)
	if err != nil {
		return nil, fmt.Errorf("failed to get resource type: %w", err)
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Call the list-resources endpoint of the specific network and use the IDs it returns, so resource and network always match.
  2. Refresh cached IDs after networks or resources are recreated.
  3. Double-check you are using a network resource ID, not a network router or route ID, in the path.

Example fix

// before
GET /api/networks/{networkA}/resources/{resB}
// after
GET /api/networks/{networkB}/resources/{resB} // resB belongs to networkB
Defensive patterns

Strategy: validation

Validate before calling

// before GetResource: confirm the resource is listed under this network
list, err := m.GetNetworkResources(ctx, accountID, userID, networkID) // or the store list call
for _, r := range list {
    if r.Id == resourceID { /* safe to call GetResource */ }
}

Try / catch

res, err := m.GetResource(ctx, accountID, userID, networkID, resourceID)
if err != nil {
    if err.Error() == "resource not part of network" {
        // treat as not-found for this network: refresh IDs, do not blind-retry
    }
    return nil, err
}

Prevention

When it happens

Trigger: GET /api/networks/{networkA-id}/resources/{resource-of-networkB-id}; a stale resourceId captured before the network or resource was recreated; scripts iterating resources across networks while reusing a single networkID.

Common situations: A UI holding a cached resource ID after the network was recreated; copy-pasting IDs between environments; confusing the network-router resource ID with the network resource ID (similar path shapes).

Related errors


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