SonarSource/sonarqube · error · AuthenticationException

Email ' ' is already used

Error message

Email '%s' is already used

What it means

UserRegistrarImpl.detectEmailUpdate resolves conflicts when an authenticating user's email already exists in the database. If more than one user already holds that email, it immediately throws 'Email <email> is already used' because the target user cannot be determined unambiguously.

Solutions

  1. Query the users table for that email and identify/merge or delete the duplicate accounts.
  2. Decide which account is legitimate and update its email; deactivate or rename the other.
  3. After cleanup, have the user log in again.
  4. Prevent recurrence by enforcing email uniqueness and cleaning up before migrations.

Example fix

// before
SELECT uuid FROM users WHERE email='a@x.com'; -- returns 2 rows
// after: deactivate the stale duplicate
UPDATE users SET active=false, email='old+a@x.com' WHERE uuid='stale-uuid';
Defensive patterns

Strategy: validation

Validate before calling

-- SQL: detect duplicate emails before login/registration fails
SELECT email, count(*) AS n FROM users WHERE active = true GROUP BY email HAVING count(*) > 1;

Try / catch

try {
  return detectEmailUpdate(dbSession, authenticatorParameters, email);
} catch (MessageException e) {
  LOG.error("Ambiguous email: multiple users already hold it; dedupe required", e);
  throw e;
}

Prevention

When it happens

Trigger: Login/registration where selectByEmail returns more than one UserDto for the email being assigned — duplicated email rows in the users table.

Common situations: Historical duplicates created before email-uniqueness enforcement; data imported/migrated from another instance; manual DB edits; case-variant duplicates from old SonarQube versions.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/af75a3e716fb7f9c. Report an issue: GitHub.

Appendix: source

Thrown at server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/UserRegistrarImpl.java:230

    userUpdater.updateAndCommit(dbSession, userDto, update, beforeCommit(dbSession, authenticatorParameters), toArray(otherUserToIndex));
    return userDto;
  }

  private Consumer<UserDto> beforeCommit(DbSession dbSession, UserRegistration authenticatorParameters) {
    return user -> syncGroups(dbSession, authenticatorParameters.getUserIdentity(), user);
  }

  private Optional<UserDto> detectEmailUpdate(DbSession dbSession, UserRegistration authenticatorParameters, @Nullable String authenticatingUserUuid) {
    String email = authenticatorParameters.getUserIdentity().getEmail();
    if (email == null) {
      return Optional.empty();
    }
    List<UserDto> existingUsers = dbClient.userDao().selectByEmail(dbSession, email);
    if (existingUsers.isEmpty()) {
      return Optional.empty();
    }
    if (existingUsers.size() > 1) {
      throw generateExistingEmailError(authenticatorParameters, email);
    }

    UserDto existingUser = existingUsers.get(0);
    if (existingUser == null || existingUser.getUuid().equals(authenticatingUserUuid)) {
      return Optional.empty();
    }
    throw generateExistingEmailError(authenticatorParameters, email);
  }

  private void syncGroups(DbSession dbSession, UserIdentity userIdentity, UserDto userDto) {
    if (!userIdentity.shouldSyncGroups()) {
      return;
    }
    String userLogin = userDto.getLogin();
    Set<String> userGroups = new HashSet<>(dbClient.groupMembershipDao().selectGroupsByLogins(dbSession, singletonList(userLogin)).get(userLogin));
    Set<String> identityGroups = userIdentity.getGroups();
    LOGGER.debug("List of groups returned by the identity provider '{}'", identityGroups);

View on GitHub (pinned to 184c821202)