bitwarden/server · error · ConflictException
ExternalId already exists for another user.
Error message
ExternalId already exists for another user.
What it means
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.
Source
Thrown at bitwarden_license/src/Scim/Users/PatchUserCommand.cs:132
}
private async Task HandleExternalIdOperationAsync(Core.Entities.OrganizationUser orgUser, string? newExternalId)
{
// Validate max length (300 chars per OrganizationUser.cs line 59)
if (!string.IsNullOrWhiteSpace(newExternalId) && newExternalId.Length > 300)
{
throw new BadRequestException("ExternalId cannot exceed 300 characters.");
}
// Check for duplicate externalId (same validation as PostUserCommand.cs)
if (!string.IsNullOrWhiteSpace(newExternalId))
{
var existingUsers = await _organizationUserRepository.GetManyDetailsByOrganizationAsync(orgUser.OrganizationId);
if (existingUsers.Any(u => u.Id != orgUser.Id &&
!string.IsNullOrWhiteSpace(u.ExternalId) &&
u.ExternalId.Equals(newExternalId, StringComparison.OrdinalIgnoreCase)))
{
throw new ConflictException("ExternalId already exists for another user.");
}
}
orgUser.ExternalId = newExternalId;
await _organizationUserRepository.ReplaceAsync(orgUser);
}
}
View on GitHub (pinned to e93b962371)
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.
Defensive patterns
Strategy: validation
Validate before calling
// Check for duplicate externalId before patching
var users = await scimClient.ListUsersAsync(orgId);
var dup = users.FirstOrDefault(u => u.ExternalId?.Equals(newExternalId, StringComparison.OrdinalIgnoreCase) == true && u.Id != userId);
if (dup != null) throw new InvalidOperationException($"externalId already used by user {dup.Id}"); Try / catch
try { await scimClient.PatchUserExternalIdAsync(orgId, userId, newExternalId); }
catch (ScimException ex) when (ex.StatusCode == 409)
{ /* resolve duplicate: change externalId or clear from other user */ } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ExternalId already exists for another group.
- User already exists.
- Conflict.
- ExternalId cannot exceed 300 characters.
- ExternalId cannot exceed 300 characters.
AI-assisted analysis of bitwarden/server@e93b962371 (2026-08-13).
Data as JSON: /api/errors/b37db3fe2dc89117.
Report an issue: GitHub.