apereo/cas · error · FailedLoginException

Missing field 'total' from the query results for [username]

Error message

Missing field 'total' from the query results for [username]

What it means

When the query result does not contain the configured password field, the handler falls back to a 'total' row-count contract: the SQL must expose a column named 'total' counting matching users. If that column is absent, it throws FailedLoginException('Missing field 'total' from the query results for [username]').

Solutions

  1. Alias the count column exactly as total: SELECT COUNT(*) AS total FROM users WHERE username=?
  2. Verify sql config returns exactly one row containing 'total' when fieldPassword is not configured
  3. Check driver/alias casing and quote the alias if needed
  4. Alternatively configure fieldPassword to the real password column to use password comparison instead

Example fix

// before
// cas.authn.jdbc.query[0].sql=SELECT COUNT(*) FROM users WHERE username=?
// after
// cas.authn.jdbc.query[0].sql=SELECT COUNT(*) AS total FROM users WHERE username=?
Defensive patterns

Strategy: validation

Validate before calling

Map<String,Object> row = jdbc.queryForMap(sql, user);
if (!row.containsKey("total")) throw new IllegalStateException("Auth SQL must expose COUNT(*) AS total when no password field is configured");

Try / catch

try {
    authResult = handler.authenticate(credential);
} catch (FailedLoginException e) {
    if (e.getMessage().contains("Missing field 'total'")) {
        log.error("Auth SQL contract violated: add SELECT ... COUNT(*) AS total");
        throw new ConfigurationException("Invalid cas.authn.jdbc.query sql"); // fail fast, this is a config bug
    }
    throw e;
}

Prevention

When it happens

Trigger: dbFields (single-row query result) lacks both properties.getFieldPassword() and the literal key 'total' — i.e. the SQL is neither a password-returning query nor a SELECT COUNT(*) AS total query.

Common situations: Misconfigured sql that selects neither password nor COUNT(*), column alias missing (SELECT COUNT(*) without 'AS total'), column alias upper-cased by the driver ('TOTAL' vs 'total'), query changed during migration.

Related errors


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

Appendix: source

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

    @Override
    protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
        final UsernamePasswordCredential credential, final String originalPassword) throws Throwable {
        val username = credential.getUsername();
        val password = credential.toPassword();
        try {
            val dbFields = query(credential);
            if (dbFields.containsKey(properties.getFieldPassword())) {
                val dbPassword = (String) dbFields.get(properties.getFieldPassword());

                val originalPasswordMatchFails = StringUtils.isNotBlank(originalPassword) && !matches(originalPassword, dbPassword);
                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");

View on GitHub (pinned to e7288fc434)