spring-projects/spring-security · error · IncorrectResultSizeDataAccessException

[N] users found with name 'X', expected 1

Error message

[N] users found with name 'X', expected 1

What it means

JdbcUserDetailsManager.userExists expects exactly zero or one row for the username; if the count query returns null or more than 1, it throws IncorrectResultSizeDataAccessException with the message '[N] users found with name X, expected 1'. This guards the invariant that usernames are unique in the users table, since duplicate rows make 'does this user exist' and subsequent loads ambiguous.

Solutions

  1. Add a UNIQUE constraint (or PRIMARY KEY) on the username column of the users table
  2. Deduplicate existing rows: DELETE duplicates keeping one row per username
  3. Ensure the configured users-by-username query cannot return more than one row per user (add filters/limits as appropriate)
  4. Catch IncorrectResultSizeDataAccessException around userExists if duplicates are legitimately possible in your environment

Example fix

// before
-- no constraint, duplicate usernames possible
CREATE TABLE users(username VARCHAR(50), ...);
// after
CREATE TABLE users(username VARCHAR(50) NOT NULL PRIMARY KEY, ...);
Defensive patterns

Strategy: try-catch

Validate before calling

Integer count = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM users WHERE username = ?", Integer.class, username);
if (count != null && count > 1) {
    throw new IllegalStateException("Duplicate username rows in users table: " + username);
}

Try / catch

try {
    return manager.userExists(username);
} catch (IncorrectResultSizeDataAccessException e) {
    // duplicates detected: trigger data-cleanup/ops alert
    return false;
}

Prevention

When it happens

Trigger: The users table contains duplicate rows for the same username (missing unique constraint on the username column); running against a schema where users is duplicated by joins or seeding scripts inserted the user twice; query configured via setUsersByUsernameQuery returning multiple rows per user.

Common situations: Manual data migration or fixtures inserting the same username twice; schemas missing PRIMARY KEY/UNIQUE on username; multi-tenant tables queried without tenant filtering in the custom query.

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


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/ea7b4fdbf5aaec7c. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/springframework/security/provisioning/JdbcUserDetailsManager.java:355

		context.setAuthentication(authentication);
		this.securityContextHolderStrategy.setContext(context);
		this.userCache.removeUserFromCache(username);
	}

	protected Authentication createNewAuthentication(Authentication currentAuth, @Nullable String newPassword) {
		UserDetails user = loadUserByUsername(currentAuth.getName());
		UsernamePasswordAuthenticationToken newAuthentication = UsernamePasswordAuthenticationToken.authenticated(user,
				null, user.getAuthorities());
		newAuthentication.setDetails(currentAuth.getDetails());
		return newAuthentication;
	}

	@Override
	public boolean userExists(String username) {
		@SuppressWarnings("ConstantConditions")
		Integer usersCount = requireJdbcTemplate().queryForObject(this.userExistsSql, Integer.class, username);
		if (usersCount == null || usersCount > 1) {
			throw new IncorrectResultSizeDataAccessException(
					"[" + usersCount + "] users found with name '" + username + "', expected 1", 1);
		}
		return usersCount == 1;
	}

	@Override
	public List<String> findAllGroups() {
		// @formatter:off
		return requireJdbcTemplate().queryForList(this.findAllGroupsSql, String.class)
			.stream()
			.filter(Objects::nonNull)
			.collect(Collectors.toList());
		// @formatter:on
	}

	@Override
	public List<String> findUsersInGroup(String groupName) {
		Assert.hasText(groupName, "groupName should have text");

View on GitHub (pinned to 96852e8860)