{"record":{"id":"89e1689b595d6676","repo":"bitwarden/server","slug":"one-or-more-collections-not-found","errorCode":null,"errorMessage":"One or more collections not found.","messagePattern":"One or more collections not found\\.","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Api/AdminConsole/Controllers/CollectionsController.cs","lineNumber":234,"sourceCode":"        }\n\n        return new CollectionAccessDetailsResponseModel(collectionWithPermissions);\n    }\n\n    [HttpPost(\"{id}\")]\n    [Obsolete(\"This endpoint is deprecated. Use PUT /{id} instead.\")]\n    public async Task<CollectionResponseModel> PostPut(Guid orgId, Guid id, [FromBody] UpdateCollectionRequestModel model)\n    {\n        return await Put(orgId, id, model);\n    }\n\n    [HttpPost(\"bulk-access\")]\n    public async Task PostBulkCollectionAccess(Guid orgId, [FromBody] BulkCollectionAccessRequestModel model)\n    {\n        var collections = await _collectionRepository.GetManyByManyIdsAsync(model.CollectionIds);\n        if (collections.Count(c => c.OrganizationId == orgId) != model.CollectionIds.Count())\n        {\n            throw new NotFoundException(\"One or more collections not found.\");\n        }\n\n        var result = await _authorizationService.AuthorizeAsync(User, collections,\n            new[] { BulkCollectionOperations.ModifyUserAccess, BulkCollectionOperations.ModifyGroupAccess });\n\n        if (!result.Succeeded)\n        {\n            throw new NotFoundException();\n        }\n\n        await _bulkAddCollectionAccessCommand.AddAccessAsync(\n            collections,\n            model.Users?.Select(u => u.ToSelectionReadOnly()).ToList(),\n            model.Groups?.Select(g => g.ToSelectionReadOnly()).ToList());\n    }\n\n    [HttpDelete(\"{id}\")]\n    public async Task Delete(Guid orgId, Guid id)","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/src/Api/AdminConsole/Controllers/CollectionsController.cs#L216-L252","documentation":"Thrown by POST /bulk-collection-access on CollectionsController when the number of collections returned by GetManyByManyIdsAsync that match the requested orgId differs from the count of ids submitted. Any id that does not exist or belongs to a different org causes the mismatch, yielding a 404 'One or more collections not found.' Unlike the no-message NotFoundException, this one carries an explicit message so the client can tell it is an input problem.","triggerScenarios":"Submitting a bulk-access request where at least one CollectionId is stale/deleted, belongs to another organization, or is malformed (and thus not returned). Duplicate ids in the request that resolve to one row can also skew the count.","commonSituations":"Bulk permission dialog left open while collections were deleted; cross-org copy of ids; client sending the same id twice; integration granting access across orgs with a flat id list.","solutions":["Pre-filter the submitted ids against the live collection list for the org to drop unknown/duplicate ids.","De-duplicate CollectionIds before submitting (the count comparison is order/count-sensitive).","Refresh the source collection selection if any id is older than the last org list load.","Scope the request to a single organization; never mix ids from multiple orgs."],"exampleFix":"// before\nmodel.CollectionIds = selectedIds;\nawait api.post(`/collections/bulk-access`, model);\n\n// after\nvar live = new HashSet<Guid>((await collectionApi.List(orgId)).Select(c => c.Id));\nmodel.CollectionIds = selectedIds.Where(i => live.Contains(i)).Distinct();\nawait api.post(`/collections/bulk-access`, model);","handlingStrategy":"validation","validationCode":"// De-duplicate and intersect submitted ids with the live org collection list\nvar live = (await collectionApi.ListAsync(orgId)).Select(c => c.Id).ToHashSet();\nmodel.CollectionIds = model.CollectionIds\n    .Where(i => live.Contains(i))\n    .Distinct()\n    .ToArray();\nif (!model.CollectionIds.Any()) return; // nothing valid to send","typeGuard":"static bool AllCollectionsExist(IEnumerable<Guid> submitted, HashSet<Guid> live)\n    => submitted.Distinct().All(i => live.Contains(i));","tryCatchPattern":"try { await api.PostAsync(\"/collections/bulk-access\", model); }\ncatch (ApiException e) when (e.StatusCode == HttpStatusCode.NotFound\n    && e.Message.Contains(\"not found\"))\n{ await refreshCollections(); throw new RetryableValidationError(e); }","preventionTips":["Always de-duplicate bulk ids — the count comparison is sensitive to duplicates.","Scope every bulk request to a single organization.","Re-resolve ids from the live list before submitting bulk operations."],"tags":["bitwarden","api","http-404","validation","collections","bulk"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}