bitwarden/server · error · NotFoundException
Resource not found.
Error message
Resource not found.
What it means
Thrown at SecretsManagerEventsController.cs:45 inside GetServiceAccountEventsAsync (GET sm/events/service-accounts/{id}). The IAuthorizationService.AuthorizeAsync for ServiceAccountOperations.ReadEvents returned Succeeded=false, so the controller throws NotFoundException (authorization-based 404). Note the serviceAccount loaded just above may be null and is passed straight into AuthorizeAsync, so a missing service account also funnels here.
Source
Thrown at src/Api/SecretsManager/Controllers/SecretsManagerEventsController.cs:45
IAuthorizationService authorizationService)
{
_authorizationService = authorizationService;
_serviceAccountRepository = serviceAccountRepository;
_eventRepository = eventRepository;
}
[HttpGet("sm/events/service-accounts/{serviceAccountId}")]
public async Task<ListResponseModel<EventResponseModel>> GetServiceAccountEventsAsync(Guid serviceAccountId,
[FromQuery] DateTime? start = null, [FromQuery] DateTime? end = null,
[FromQuery] string continuationToken = null)
{
var serviceAccount = await _serviceAccountRepository.GetByIdAsync(serviceAccountId);
var authorizationResult =
await _authorizationService.AuthorizeAsync(User, serviceAccount, ServiceAccountOperations.ReadEvents);
if (!authorizationResult.Succeeded)
{
throw new NotFoundException();
}
var dateRange = ApiHelpers.GetDateRange(start, end);
var result = await _eventRepository.GetManyByOrganizationServiceAccountAsync(serviceAccount.OrganizationId,
serviceAccount.Id, dateRange.Item1, dateRange.Item2,
new PageOptions { ContinuationToken = continuationToken });
var responses = result.Data.Select(e => new EventResponseModel(e));
return new ListResponseModel<EventResponseModel>(responses, result.ContinuationToken);
}
}
View on GitHub (pinned to e93b962371)
Solutions
- Ensure the caller has ReadEvents permission on the service account (org admin or granted access policy).
- Confirm the service account id is current and belongs to an org the caller can access.
- Fetch the service account list first to validate the id before requesting its events.
Example fix
// before: assume access to any service account's events
await client.GetAsync($"/sm/events/service-accounts/{saId}"); // 404
// after: verify the id is in the caller's readable service accounts
var visible = await ListVisibleServiceAccountsAsync();
if (visible.Any(sa => sa.Id == saId))
await client.GetAsync($"/sm/events/service-accounts/{saId}"); Defensive patterns
Strategy: validation
Validate before calling
// Confirm the service account id is visible to the caller first
var visible = await ListVisibleServiceAccountsAsync();
if (!visible.Any(sa => sa.Id == serviceAccountId))
throw new UnauthorizedAccessException("Cannot read events for this service account");
await client.GetAsync($"/sm/events/service-accounts/{serviceAccountId}"); Try / catch
try { await client.GetAsync($"/sm/events/service-accounts/{id}"); }
catch (ApiException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { Log.Warn("Events access denied or SA missing"); } Prevention
- Restrict event-reading UI to admins or granted principals.
- Validate the service account id against the readable list first.
- Watch for stale/deleted service account ids.
When it happens
Trigger: GET /sm/events/service-accounts/{id} by a caller without permission to read that service account's events, or where the service account id does not exist.
Common situations: User is not an admin/manager of the service account's organization; service account id is stale/deleted; principal lacks the events-read access policy.
Related errors
- Only service accounts can sync secrets.
- Resource not found.
- Resource not found.
- Unauthorized.
- Resource not found.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/35d27951e425cfc5.
Report an issue: GitHub.