bitwarden/server · error · NotFoundException
One or more collections not found.
Error message
One or more collections not found.
What it means
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.
Source
Thrown at src/Api/AdminConsole/Controllers/CollectionsController.cs:234
}
return new CollectionAccessDetailsResponseModel(collectionWithPermissions);
}
[HttpPost("{id}")]
[Obsolete("This endpoint is deprecated. Use PUT /{id} instead.")]
public async Task<CollectionResponseModel> PostPut(Guid orgId, Guid id, [FromBody] UpdateCollectionRequestModel model)
{
return await Put(orgId, id, model);
}
[HttpPost("bulk-access")]
public async Task PostBulkCollectionAccess(Guid orgId, [FromBody] BulkCollectionAccessRequestModel model)
{
var collections = await _collectionRepository.GetManyByManyIdsAsync(model.CollectionIds);
if (collections.Count(c => c.OrganizationId == orgId) != model.CollectionIds.Count())
{
throw new NotFoundException("One or more collections not found.");
}
var result = await _authorizationService.AuthorizeAsync(User, collections,
new[] { BulkCollectionOperations.ModifyUserAccess, BulkCollectionOperations.ModifyGroupAccess });
if (!result.Succeeded)
{
throw new NotFoundException();
}
await _bulkAddCollectionAccessCommand.AddAccessAsync(
collections,
model.Users?.Select(u => u.ToSelectionReadOnly()).ToList(),
model.Groups?.Select(g => g.ToSelectionReadOnly()).ToList());
}
[HttpDelete("{id}")]
public async Task Delete(Guid orgId, Guid id)View on GitHub (pinned to e93b962371)
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.
Example fix
// before model.CollectionIds = selectedIds; await api.post(`/collections/bulk-access`, model); // after var live = new HashSet<Guid>((await collectionApi.List(orgId)).Select(c => c.Id)); model.CollectionIds = selectedIds.Where(i => live.Contains(i)).Distinct(); await api.post(`/collections/bulk-access`, model);
Defensive patterns
Strategy: validation
Validate before calling
// De-duplicate and intersect submitted ids with the live org collection list
var live = (await collectionApi.ListAsync(orgId)).Select(c => c.Id).ToHashSet();
model.CollectionIds = model.CollectionIds
.Where(i => live.Contains(i))
.Distinct()
.ToArray();
if (!model.CollectionIds.Any()) return; // nothing valid to send Type guard
static bool AllCollectionsExist(IEnumerable<Guid> submitted, HashSet<Guid> live)
=> submitted.Distinct().All(i => live.Contains(i)); Try / catch
try { await api.PostAsync("/collections/bulk-access", model); }
catch (ApiException e) when (e.StatusCode == HttpStatusCode.NotFound
&& e.Message.Contains("not found"))
{ await refreshCollections(); throw new RetryableValidationError(e); } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Resource not found.
- Resource not found.
- Requested collections must belong to the same organization.
- Last synced date must be in the past.
- Only service accounts can sync secrets.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/89e1689b595d6676.
Report an issue: GitHub.