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.

Solutions

  1. List users via GET /v2/{organizationId}/Users to confirm whether the user still exists.
  2. If removed, re-provision via POST or remove the stale reference from the IdP.
  3. Verify the organizationId matches the SCIM token's organization scope.
  4. 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

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


AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13). Data as JSON: /api/errors/87193588d1745e85. Report an issue: GitHub.

Appendix: 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/revoke

View on GitHub (pinned to e93b962371)