bitwarden/server · error · NotFoundException

Resource not found.

Error message

Resource not found.

What it means

Thrown by PUT /{id} on GroupsController inside the per-collection ModifyGroupAccess authorization loop: if any posted collection fails AuthorizeAsync(BulkCollectionOperations.ModifyGroupAccess), the update aborts with a 404. This is distinct from the earlier org/existence 404 (132) — it fires only after the self-add guard passes and means the caller lacks manage-access on at least one collection in the save.

Source

Thrown at src/Api/AdminConsole/Controllers/GroupsController.cs:182

            var currentGroupUsers = await _groupRepository.GetManyUserIdsByIdAsync(id);
            // OrganizationUser may be null if the current user is a provider
            if (organizationUser != null && !currentGroupUsers.Contains(organizationUser.Id) && model.Users.Contains(organizationUser.Id))
            {
                throw new BadRequestException("You cannot add yourself to groups.");
            }
        }

        // Authorization check:
        // You must have authorization to ModifyUserAccess for all collections being saved
        var postedCollections = await _collectionRepository
            .GetManyByManyIdsAsync(model.Collections.Select(c => c.Id));
        foreach (var collection in postedCollections)
        {
            if (!(await _authorizationService.AuthorizeAsync(User, collection,
                    BulkCollectionOperations.ModifyGroupAccess))
                .Succeeded)
            {
                throw new NotFoundException();
            }
        }

        // The client only sends collections that the saving user has permissions to edit.
        // We need to combine these with collections that the user doesn't have permissions for, so that we don't
        // accidentally overwrite those
        var currentCollections = await _collectionRepository
            .GetManyByManyIdsAsync(currentAccess.Select(cas => cas.Id));

        var readonlyCollectionIds = new HashSet<Guid>();
        foreach (var collection in currentCollections)
        {
            if (!(await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.ModifyGroupAccess))
                .Succeeded)
            {
                readonlyCollectionIds.Add(collection.Id);
            }
        }

View on GitHub (pinned to e93b962371)

Solutions

  1. Restrict the submitted Collections to those the caller can manage group access on; the server already preserves read-only assignments separately.
  2. Use an owner/admin token for cross-collection group edits.
  3. On 404 here (post self-add check), re-evaluate ModifyGroupAccess per collection and drop disallowed ones.
  4. Refresh collection rights before showing the edit form so disallowed collections are not re-submitted.

Example fix

// before
model.Collections = currentAndPostedCollections;
await api.put(`/organizations/${orgId}/groups/${id}`, model);

// after
model.Collections = currentAndPostedCollections
    .Where(c => await canModifyGroupAccess(c.Id)).ToList();
await api.put(`/organizations/${orgId}/groups/${id}`, model);
Defensive patterns

Strategy: validation

Validate before calling

// Restrict submitted collections to those the caller can manage group access on
model.Collections = model.Collections
    .Where(c => await accessProbe.CanModifyGroupAccess(c.Id))
    .ToList();
// The server preserves read-only assignments server-side, so omitting is safe

Type guard

static bool CanManageAllPosted(IEnumerable<CollectionAccess> cols)
    => cols.All(c => c.CanModifyGroupAccess);

Try / catch

try { await api.PutAsync($"/groups/{id}", model); }
catch (ApiException e) when (e.StatusCode == HttpStatusCode.NotFound)
{ // fired after self-add guard => permission issue, not missing group
  model.Collections = await filterManageable(model.Collections); await retryOnce(); }

Prevention

When it happens

Trigger: Saving a group whose Collections set includes one or more collections the caller cannot grant group access to (read-only collection, cross-org collection, restricted-by-custom-role collection).

Common situations: Editor pre-loaded existing assignments that include a collection the caller lost rights to; manager editing a group that spans collections beyond their access; client sending the full current collection set back without filtering.

Related errors


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/35124df342a32a54. Report an issue: GitHub.