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
- Query the users table for that email and identify/merge or delete the duplicate accounts.
- Decide which account is legitimate and update its email; deactivate or rename the other.
- After cleanup, have the user log in again.
- 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
- Run a duplicate-email audit before SonarQube upgrades
- Enforce email uniqueness when importing users
- Merge or deactivate duplicate accounts promptly
- Never hand-edit the users table without a backup
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
- Analysis Export failed after processing
- Analysis report part is missing in database
- Authentication is required
- Branch export failed after processing
- Can not connect to database. Please check connectivity and…
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)