bitwarden/server · error · NotFoundException
User not found.
Error message
User not found.
What it means
Thrown as a NotFoundException (HTTP 404) by PatchUserCommand.PatchUserAsync when the organization user for {id} does not exist or its OrganizationId does not match the route's organizationId. This is the initial existence check before processing any PATCH operations on the user.
Source
Thrown at bitwarden_license/src/Scim/Users/PatchUserCommand.cs:35
private readonly IRevokeOrganizationUserCommand _revokeOrganizationUserCommand;
public PatchUserCommand(IOrganizationUserRepository organizationUserRepository,
IRestoreOrganizationUserCommand restoreOrganizationUserCommand,
ILogger<PatchUserCommand> logger,
IRevokeOrganizationUserCommand revokeOrganizationUserCommand)
{
_organizationUserRepository = organizationUserRepository;
_restoreOrganizationUserCommand = restoreOrganizationUserCommand;
_logger = logger;
_revokeOrganizationUserCommand = revokeOrganizationUserCommand;
}
public async Task PatchUserAsync(Guid organizationId, Guid id, ScimPatchModel model)
{
var orgUser = await _organizationUserRepository.GetByIdAsync(id);
if (orgUser == null || orgUser.OrganizationId != organizationId)
{
throw new NotFoundException("User not found.");
}
var operationHandled = false;
foreach (var operation in model.Operations)
{
// Replace operations
if (operation.Op?.ToLowerInvariant() == PatchOps.Replace)
{
// Active from path
if (operation.Path?.ToLowerInvariant() == "active")
{
var active = operation.Value.ToString()?.ToLowerInvariant();
var handled = await HandleActiveOperationAsync(orgUser, active == "true");
if (!operationHandled)
{
operationHandled = handled;
}
// Re-fetch to pick up status changes persisted by restore/revokeView on GitHub (pinned to e93b962371)
Solutions
- List users via GET /v2/{organizationId}/Users to confirm whether the user still exists.
- If removed, re-provision via POST or remove the stale reference from the IdP.
- Verify the organizationId matches the SCIM token's organization scope.
- Re-sync the IdP directory to reconcile.
Defensive patterns
Strategy: validation
Validate before calling
var existing = await scimClient.GetUserAsync(orgId, userId);
if (existing == null) { /* user gone — reconcile IdP ref */ return; }
// only then PATCH Try / catch
try { await scimClient.PatchUserAsync(orgId, userId, patchModel); }
catch (ScimException ex) when (ex.StatusCode == 404)
{ /* user removed — delete from IdP or re-provision */ } Prevention
- Cache user GUIDs and prune entries that 404.
- Run periodic full-sync reconciliation.
- Never blindly retry PATCH on a 404 — the user is gone.
When it happens
Trigger: PATCH /v2/{organizationId}/Users/{id} where the organization user was deleted, revoked and purged, or belongs to a different org. IdP sends an update (e.g., deactivate) for a user no longer in Bitwarden.
Common situations: User removed from Bitwarden but IdP still tracks them. IdP sends a deactivation PATCH after the user was already deleted. Cross-tenant GUID confusion from wrong SCIM key.
Related errors
- User not found.
- Group not found.
- ExternalId cannot exceed 300 characters.
- ExternalId already exists for another user.
- ExternalId cannot exceed 300 characters.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/87193588d1745e85.
Report an issue: GitHub.