{"record":{"id":"b37db3fe2dc89117","repo":"bitwarden/server","slug":"externalid-already-exists-for-another-user","errorCode":null,"errorMessage":"ExternalId already exists for another user.","messagePattern":"ExternalId already exists for another user\\.","errorType":"exception","errorClass":"ConflictException","httpStatus":409,"severity":"error","filePath":"bitwarden_license/src/Scim/Users/PatchUserCommand.cs","lineNumber":132,"sourceCode":"    }\n\n    private async Task HandleExternalIdOperationAsync(Core.Entities.OrganizationUser orgUser, string? newExternalId)\n    {\n        // Validate max length (300 chars per OrganizationUser.cs line 59)\n        if (!string.IsNullOrWhiteSpace(newExternalId) && newExternalId.Length > 300)\n        {\n            throw new BadRequestException(\"ExternalId cannot exceed 300 characters.\");\n        }\n\n        // Check for duplicate externalId (same validation as PostUserCommand.cs)\n        if (!string.IsNullOrWhiteSpace(newExternalId))\n        {\n            var existingUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgUser.OrganizationId);\n            if (existingUsers.Any(u => u.Id != orgUser.Id &&\n                !string.IsNullOrWhiteSpace(u.ExternalId) &&\n                u.ExternalId.Equals(newExternalId, StringComparison.OrdinalIgnoreCase)))\n            {\n                throw new ConflictException(\"ExternalId already exists for another user.\");\n            }\n        }\n\n        orgUser.ExternalId = newExternalId;\n        await _organizationUserRepository.ReplaceAsync(orgUser);\n    }\n}\n","sourceCodeStart":114,"sourceCodeEnd":140,"githubUrl":"https://github.com/bitwarden/server/blob/e93b962371d80964556f5590c6615f5160a437a1/bitwarden_license/src/Scim/Users/PatchUserCommand.cs#L114-L140","documentation":"Thrown as a ConflictException (HTTP 409) by PatchUserCommand.HandleExternalIdOperationAsync when another organization user in the same org already has the same externalId (case-insensitive comparison, excluding the user being patched). ExternalId must be unique within an organization for reliable IdP-to-Bitwarden user mapping.","triggerScenarios":"PATCH /v2/{organizationId}/Users/{id} with a 'replace' on 'externalId' that collides with another user's externalId in the same org. Happens after a directory merge, user rename, or re-import with overlapping identifiers.","commonSituations":"Two directory entries share the same externalId after a restructure. An admin duplicated a user's externalId. Case-variant collisions from IdPs that normalize externalId differently.","solutions":["Find the conflicting user: GET /v2/{organizationId}/Users?filter=externalId eq \"...\".","Change the externalId to a unique value, or clear it from the conflicting user first.","Fix the IdP attribute mapping so each user gets a distinct externalId.","Run a deduplication pass on the source directory."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Check for duplicate externalId before patching\nvar users = await scimClient.ListUsersAsync(orgId);\nvar dup = users.FirstOrDefault(u => u.ExternalId?.Equals(newExternalId, StringComparison.OrdinalIgnoreCase) == true && u.Id != userId);\nif (dup != null) throw new InvalidOperationException($\"externalId already used by user {dup.Id}\");","typeGuard":null,"tryCatchPattern":"try { await scimClient.PatchUserExternalIdAsync(orgId, userId, newExternalId); }\ncatch (ScimException ex) when (ex.StatusCode == 409)\n{ /* resolve duplicate: change externalId or clear from other user */ }","preventionTips":["Deduplicate externalIds across all users in the source directory.","Run a uniqueness check before syncing externalId changes.","Treat 409 on externalId as requiring manual resolution."],"tags":["scim","conflict","users","http-409","externalid","uniqueness","patch"],"backgroundTag":null,"analyzedSha":"e93b962371d80964556f5590c6615f5160a437a1","analyzedAt":"2026-08-13T14:22:19.382Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}