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

  1. Find the conflicting user: GET /v2/{organizationId}/Users?filter=externalId eq "...".
  2. Change the externalId to a unique value, or clear it from the conflicting user first.
  3. Fix the IdP attribute mapping so each user gets a distinct externalId.
  4. 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

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


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