apereo/cas · error · FailedLoginException

No records found for user [username]

Error message

No records found for user [username]

What it means

Using the count-based contract, the handler throws FailedLoginException('No records found for user [username]') when the 'total' value parses but is not exactly 1. Only a single matching record authenticates; zero matches (unknown user) and multiple matches (bad schema) both land here.

Solutions

  1. Confirm the user exists if count is 0 (data or username problem)
  2. Deduplicate rows and enforce a unique constraint if count > 1
  3. Check the SQL WHERE clause matches exactly one row per user
  4. Add explicit AccountNotFoundException handling upstream if you need to distinguish 0 from >1

Example fix

// before: duplicates allowed
// CREATE TABLE users(username VARCHAR(255), ...);
// after
// CREATE UNIQUE INDEX ux_users_username ON users(username);
Defensive patterns

Strategy: validation

Validate before calling

int total = jdbc.queryForObject("SELECT COUNT(*) FROM users WHERE username=?", Integer.class, user);
if (total != 1) throw new IllegalStateException("Expected exactly 1 user row for " + user + ", found " + total);

Try / catch

try {
    authResult = handler.authenticate(credential);
} catch (FailedLoginException e) {
    if (e.getMessage().startsWith("No records found")) {
        // 0 => unknown user, >1 => duplicates; both are data issues, return generic failure
        audit.logNoSingleRecord(user);
        throw new BadCredentialsException("Invalid credentials");
    }
    throw e;
}

Prevention

When it happens

Trigger: NumberUtils.createNumber(total.toString()).longValue() != 1 — typically COUNT(*) = 0 because no row matches the username, or > 1 because of duplicates.

Common situations: Unknown user (count 0), duplicate usernames without a unique constraint (count > 1), case-insensitive duplicates, wrong WHERE clause joining multiple rows.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/ece2de7500d15efc. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-jdbc-authentication/src/main/java/org/apereo/cas/jdbc/QueryDatabaseAuthenticationHandler.java:79

                val originalPasswordEquals = StringUtils.isBlank(originalPassword) && !Strings.CI.equals(password, dbPassword);
                if (originalPasswordMatchFails || originalPasswordEquals) {
                    throw new FailedLoginException("Password does not match value on record.");
                }
            } else {
                LOGGER.debug("Password field is not found in the query results. Checking for result count...");
                if (!dbFields.containsKey("total")) {
                    throw new FailedLoginException("Missing field 'total' from the query results for " + username);
                }

                val count = dbFields.get("total");
                if (count == null || !NumberUtils.isCreatable(count.toString())) {
                    throw new FailedLoginException("Missing field value 'total' from the query results for "
                        + username + " or value not parseable as a number");
                }

                val number = NumberUtils.createNumber(count.toString());
                if (number.longValue() != 1) {
                    throw new FailedLoginException("No records found for user " + username);
                }
            }

            if (StringUtils.isNotBlank(properties.getFieldDisabled()) && dbFields.containsKey(properties.getFieldDisabled())) {
                val dbDisabled = dbFields.get(properties.getFieldDisabled()).toString();
                if (BooleanUtils.toBoolean(dbDisabled) || "1".equals(dbDisabled)) {
                    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);

View on GitHub (pinned to e7288fc434)