apereo/cas · error · LoginException

Multiple principal values are not allowed: [principalAttr]

Error message

Multiple principal values are not allowed: [principalAttr]

What it means

LdapAuthenticationHandler reads the principal id attribute from the LDAP entry returned by bind/search. If the attribute holds more than one value and allowMultiplePrincipalAttributeValues is false (the default), the handler aborts the login with a LoginException rather than guess which value identifies the principal.

Solutions

  1. Set cas.authn.ldap[x].allow-multiple-principal-attribute-values=true so the first value is used with a warning
  2. Clean the directory entry so the principal id attribute has exactly one value
  3. Point principalIdAttribute at an attribute guaranteed single-valued (e.g. uid, sAMAccountName, entryUUID)
  4. Add the search filter a constraint (or refine base DN) so only entries with a single value match

Example fix

// before (application.yml)
cas.authn.ldap[0].principal-attribute-list=mail
// after
cas.authn.ldap[0].principal-attribute-list=mail
cas.authn.ldap[0].allow-multiple-principal-attribute-values=true
Defensive patterns

Strategy: validation

Validate before calling

// in directory management / before enabling handler
String filter = "(uid=" + username + ")";
SearchResult entry = LdapUtils.searchForEntry(props, filter);
Attribute attr = entry.getAttribute(principalIdAttr);
if (attr != null && attr.size() > 1) {
    throw new IllegalStateException("Principal id attribute is multivalued for " + username);
}

Try / catch

try {
    handler.authenticate(transaction);
} catch (LoginException e) {
    if (e.getMessage().startsWith("Multiple principal values are not allowed")) {
        // flag entry for directory cleanup or enable allow-multiple-principal-attribute-values
    }
}

Prevention

When it happens

Trigger: Configured principalIdAttribute (e.g. uid, mail) is multivalued in the directory for the authenticating user — common with mail, memberOf-style attributes, or entries that merged two records — while cas.authn.ldap[x].allow-multiple-principal-attribute-values is not enabled.

Common situations: Directory cleanup after org merges leaves a user with two mail values; admin points principalIdAttribute at a multivalued attribute like mail or employeeType; upgrading CAS to a stricter handler version surfaces previously hidden duplicate values.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-ldap-core/src/main/java/org/apereo/cas/authentication/LdapAuthenticationHandler.java:213

        if (StringUtils.isNotBlank(this.principalIdAttribute)) {
            val principalAttr = ldapEntry.getAttribute(this.principalIdAttribute);
            if (principalAttr == null || principalAttr.size() == 0) {
                if (this.allowMissingPrincipalAttributeValue) {
                    LOGGER.warn("The principal id attribute [{}] is not found. CAS cannot construct the final authenticated principal "
                            + "if it's unable to locate the attribute that is designated as the principal id. "
                            + "Attributes available on the LDAP entry are [{}]. Since principal id attribute is not available, CAS will "
                            + "fall back to construct the principal based on the provided user id: [{}]",
                        this.principalIdAttribute, ldapEntry.getAttributes(), username);
                    return username;
                }
                LOGGER.error("The principal id attribute [{}] is not found. CAS is configured to disallow missing principal attributes",
                    this.principalIdAttribute);
                throw new LoginException("Principal id attribute is not found for " + principalAttr);
            }
            val value = principalAttr.getStringValue();
            if (principalAttr.size() > 1) {
                if (!this.allowMultiplePrincipalAttributeValues) {
                    throw new LoginException("Multiple principal values are not allowed: " + principalAttr);
                }
                LOGGER.warn("Found multiple values for principal id attribute: [{}]. Using first value=[{}].", principalAttr, value);
            }
            LOGGER.debug("Retrieved principal id attribute [{}]", value);
            return value;
        }
        LOGGER.debug("Principal id attribute is not defined. Using the default provided user id [{}]", username);
        return username;
    }

    private AuthenticationResponse getLdapAuthenticationResponse(final UsernamePasswordCredential upc) throws PreventedException {
        try {
            LOGGER.debug("Attempting LDAP authentication for [{}]. Authenticator pre-configured attributes are [{}], "
                    + "additional requested attributes for this authentication request are [{}]", upc, authenticator.getReturnAttributes(),
                authenticatedEntryAttributes);
            var ldaptiveCred = new Credential(upc.getPassword());
            val request = new AuthenticationRequest(upc.getUsername(), ldaptiveCred, authenticatedEntryAttributes);
            request.setControls(new PasswordPolicyControl());

View on GitHub (pinned to e7288fc434)