{"record":{"id":"35124df342a32a54","repo":"bitwarden/server","slug":"resource-not-found-35124d","errorCode":null,"errorMessage":"Resource not found.","messagePattern":"Resource not found\\.","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Api/AdminConsole/Controllers/GroupsController.cs","lineNumber":182,"sourceCode":"            var currentGroupUsers = await _groupRepository.GetManyUserIdsByIdAsync(id);\n            // OrganizationUser may be null if the current user is a provider\n            if (organizationUser != null && !currentGroupUsers.Contains(organizationUser.Id) && model.Users.Contains(organizationUser.Id))\n            {\n                throw new BadRequestException(\"You cannot add yourself to groups.\");\n            }\n        }\n\n        // Authorization check:\n        // You must have authorization to ModifyUserAccess for all collections being saved\n        var postedCollections = await _collectionRepository\n            .GetManyByManyIdsAsync(model.Collections.Select(c => c.Id));\n        foreach (var collection in postedCollections)\n        {\n            if (!(await _authorizationService.AuthorizeAsync(User, collection,\n                    BulkCollectionOperations.ModifyGroupAccess))\n                .Succeeded)\n            {\n                throw new NotFoundException();\n            }\n        }\n\n        // The client only sends collections that the saving user has permissions to edit.\n        // We need to combine these with collections that the user doesn't have permissions for, so that we don't\n        // accidentally overwrite those\n        var currentCollections = await _collectionRepository\n            .GetManyByManyIdsAsync(currentAccess.Select(cas => cas.Id));\n\n        var readonlyCollectionIds = new HashSet<Guid>();\n        foreach (var collection in currentCollections)\n        {\n            if (!(await _authorizationService.AuthorizeAsync(User, collection, BulkCollectionOperations.ModifyGroupAccess))\n                .Succeeded)\n            {\n                readonlyCollectionIds.Add(collection.Id);\n            }\n        }","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/AdminConsole/Controllers/GroupsController.cs#L164-L200","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Restrict the submitted Collections to those the caller can manage group access on; the server already preserves read-only assignments separately.","Use an owner/admin token for cross-collection group edits.","On 404 here (post self-add check), re-evaluate ModifyGroupAccess per collection and drop disallowed ones.","Refresh collection rights before showing the edit form so disallowed collections are not re-submitted."],"exampleFix":"// before\nmodel.Collections = currentAndPostedCollections;\nawait api.put(`/organizations/${orgId}/groups/${id}`, model);\n\n// after\nmodel.Collections = currentAndPostedCollections\n    .Where(c => await canModifyGroupAccess(c.Id)).ToList();\nawait api.put(`/organizations/${orgId}/groups/${id}`, model);","handlingStrategy":"validation","validationCode":"// Restrict submitted collections to those the caller can manage group access on\nmodel.Collections = model.Collections\n    .Where(c => await accessProbe.CanModifyGroupAccess(c.Id))\n    .ToList();\n// The server preserves read-only assignments server-side, so omitting is safe","typeGuard":"static bool CanManageAllPosted(IEnumerable<CollectionAccess> cols)\n    => cols.All(c => c.CanModifyGroupAccess);","tryCatchPattern":"try { await api.PutAsync($\"/groups/{id}\", model); }\ncatch (ApiException e) when (e.StatusCode == HttpStatusCode.NotFound)\n{ // fired after self-add guard => permission issue, not missing group\n  model.Collections = await filterManageable(model.Collections); await retryOnce(); }","preventionTips":["Re-submit only collections the caller can manage access on; the server keeps the rest.","Refresh collection rights before loading the edit form.","Differentiate this 404 (post self-add check) from the existence 404."],"tags":["bitwarden","api","http-404","authorization","groups","collections"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}