apereo/cas · error · FailedLoginException
Multiple records found for [username]
Error message
Multiple records found for [username]
What it means
When IncorrectResultSizeDataAccessException reports more than one row (actualSize > 1), the handler throws FailedLoginException 'Multiple records found'. The query is expected to return exactly one row per username; duplicates make identity ambiguous so login is refused.
Solutions
- Deduplicate rows: SELECT username, COUNT(*) FROM users GROUP BY username HAVING COUNT(*)>1, then remove/disable extras.
- Add a UNIQUE index on the username column to prevent recurrence.
- Refine the SQL to disambiguate (extra WHERE condition, status=active filter).
- Fix upstream provisioning that created the duplicate account.
Example fix
// before: no constraint, duplicates possible // after DELETE FROM users WHERE id NOT IN (SELECT MIN(id) FROM users GROUP BY username); ALTER TABLE users ADD CONSTRAINT uq_users_username UNIQUE (username);
Defensive patterns
Strategy: validation
Validate before calling
List<String> dupes = jdbcTemplate.queryForList(
"SELECT username FROM users GROUP BY username HAVING COUNT(*)>1", String.class);
if (!dupes.isEmpty()) { throw new IllegalStateException("Duplicate usernames: " + dupes); } Try / catch
try { result = handler.authenticate(credential); }
catch (FailedLoginException e) { if (e.getMessage().startsWith("Multiple records")) { alertDataIntegrity(); } } Prevention
- Enforce a UNIQUE constraint on the username column.
- Filter soft-deleted/legacy rows out of the auth query.
- Run periodic duplicate-detection queries as a data-integrity check.
When it happens
Trigger: sqlFindByUsername matches 2+ rows for the submitted username because the username column is not unique in the table.
Common situations: Missing UNIQUE constraint on the username column after data import; soft-deleted duplicate rows; username colliding across tenants in a shared table.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- [username] not found with SQL query
- [username] not found with SQL query.
- [e.getMessage()]
- Principal attribute [
- [e.getMessage()]
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/9fbace52c4f27e93.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-jdbc-authentication/src/main/java/org/apereo/cas/jdbc/QueryDatabaseAuthenticationHandler.java:104
throw new AccountDisabledException("Account has been disabled");
}
}
if (StringUtils.isNotBlank(properties.getFieldExpired()) && dbFields.containsKey(properties.getFieldExpired())) {
val dbExpired = dbFields.get(properties.getFieldExpired()).toString();
if (BooleanUtils.toBoolean(dbExpired) || "1".equals(dbExpired)) {
throw new AccountPasswordMustChangeException("Password has expired");
}
}
val attributes = collectPrincipalAttributes(dbFields);
val principal = this.principalFactory.createPrincipal(username, attributes);
return createHandlerResult(credential, principal, new ArrayList<>());
} catch (final IncorrectResultSizeDataAccessException e) {
if (e.getActualSize() == 0) {
throw new AccountNotFoundException(username + " not found with SQL query");
}
throw new FailedLoginException("Multiple records found for " + username);
} catch (final DataAccessException e) {
throw new PreventedException(e);
}
}
protected Map<String, Object> query(final UsernamePasswordCredential credential) {
val sql = SpringExpressionLanguageValueResolver.getInstance().resolve(properties.getSql());
if (sql.contains("?")) {
return getJdbcTemplate().queryForMap(sql, credential.getUsername());
}
val parameters = new LinkedHashMap<String, Object>();
parameters.put("username", credential.getUsername());
parameters.put("password", credential.toPassword());
return getNamedParameterJdbcTemplate().queryForMap(sql, parameters);
}
}
View on GitHub (pinned to e7288fc434)