{"record":{"id":"ff51acf441b6b779","repo":"bitwarden/server","slug":"user-already-exists","errorCode":null,"errorMessage":"User already exists.","messagePattern":"User already exists\\.","errorType":"exception","errorClass":"ConflictException","httpStatus":409,"severity":"error","filePath":"bitwarden_license/src/Scim/Users/PostUserCommand.cs","lineNumber":119,"sourceCode":"\n        if (organization is null)\n        {\n            throw new NotFoundException();\n        }\n\n        var request = model.ToRequest(\n            scimProvider: scimProvider,\n            organization: organization,\n            performedAt: timeProvider.GetUtcNow());\n\n        var orgUsers = await organizationUserRepository\n            .GetManyDetailsByOrganizationAsync(request.Organization.Id);\n\n        if (orgUsers.Any(existingUser =>\n                request.Invites.First().Email.Equals(existingUser.Email, StringComparison.OrdinalIgnoreCase) ||\n                request.Invites.First().ExternalId.Equals(existingUser.ExternalId, StringComparison.OrdinalIgnoreCase)))\n        {\n            throw new ConflictException(\"User already exists.\");\n        }\n\n        var result = await inviteOrganizationUsersCommand.InviteScimOrganizationUserAsync(request);\n\n        var invitedOrganizationUserId = result switch\n        {\n            Success<ScimInviteOrganizationUsersResponse> success => success.Value.InvitedUser.Id,\n            Failure<ScimInviteOrganizationUsersResponse> { Error.Message: NoUsersToInviteError.Code } => (Guid?)null,\n            Failure<ScimInviteOrganizationUsersResponse> failure => throw MapToBitException(failure.Error),\n            _ => throw new InvalidOperationException()\n        };\n\n        var organizationUser = invitedOrganizationUserId.HasValue\n            ? await organizationUserRepository.GetDetailsByIdAsync(invitedOrganizationUserId.Value)\n            : null;\n\n        return organizationUser;\n    }","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/bitwarden_license/src/Scim/Users/PostUserCommand.cs#L101-L137","documentation":"Thrown as a ConflictException (HTTP 409) by PostUserCommand.InviteScimOrganizationUserAsync_vNext when an existing organization user already has the same email (case-insensitive) or the same externalId (case-insensitive). This is the pre-invite deduplication check in the vNext invitation path.","triggerScenarios":"POST /v2/{organizationId}/Users where the email or externalId in the invite matches an existing user in the org. Common when the IdP re-sends a user creation that already succeeded or when two directory entries resolve to the same email/externalId.","commonSituations":"IdP retries a create after a timeout where the first succeeded. User was invited manually and the IdP now also tries to provision them. Email aliases or case variations cause false matches. Directory has duplicate entries.","solutions":["Check existing users: GET /v2/{organizationId}/Users?filter=emails.value eq \"...\" or filter by externalId.","If the user exists, PATCH them instead of creating a new one.","Configure the IdP to treat 409 as 'already exists' and skip or update.","Deduplicate the source directory for both email and externalId."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before creating, check if user exists by email or externalId\nvar existing = await scimClient.ListUsersAsync(orgId, filter: $\"emails.value eq \\\"{email}\\\"\");\nif (!existing.Any()) existing = await scimClient.ListUsersAsync(orgId, filter: $\"externalId eq \\\"{externalId}\\\"\");\nif (existing.Any()) { /* PATCH instead of POST */ }","typeGuard":null,"tryCatchPattern":"try { await scimClient.CreateUserAsync(orgId, model); }\ncatch (ScimException ex) when (ex.StatusCode == 409)\n{ // user likely exists — fetch and PATCH instead\n  var existing = await scimClient.ListUsersAsync(orgId, filter: $\"emails.value eq \\\"{email}\\\"\");\n  if (existing.Any()) await scimClient.PatchUserAsync(orgId, existing.First().Id, patchModel); }","preventionTips":["Implement idempotent user creation: on 409, fall back to lookup-and-PATCH.","Deduplicate emails and externalIds in the source directory.","Configure the IdP to treat 409 as 'already exists' and skip or update."],"tags":["scim","conflict","users","http-409","create","uniqueness","email","externalid"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}