apereo/cas · warning

Could not locate LDAP attribute

Error message

Could not locate LDAP attribute [{}] for [{}]

What it means

The internal findAttribute helper searches the LDAP entry returned for the user and reads the requested attribute's string value. When the entry exists but does not contain the configured attribute (attr == null), it logs this warn and returns null, propagating 'attribute missing' to findEmails/findPhone/findUsername callers.

Solutions

  1. Verify the attribute exists and is populated on the user's DN in the directory (ldapsearch as the configured bind user)
  2. Fix cas.authn.pm.reset.mail.attributeName / sms.attributeName to match the directory schema exactly
  3. Extend the entry's objectClass or populate the attribute for affected users
  4. Check ACLs so the PM bind account can read the attribute

Example fix

// before
cas.authn.pm.reset.sms.attributeName=telephoneNumber
// entries only populate 'mobile'
// after
cas.authn.pm.reset.sms.attributeName=mobile
Defensive patterns

Strategy: fallback

Validate before calling

// pre-flight: confirm the attribute is readable before triggering flows
Attributes a = ctx.getAttributes(userDn, new String[]{attributeName});
if (a.get(attributeName) == null) {
    log.warn("User {} has no {} attribute", userDn, attributeName);
}

Try / catch

String v = pmService.findPhone(query);
if (v == null) { log.info("No phone attribute; falling back to email flow"); }

Prevention

When it happens

Trigger: LDAP search succeeds and returns the user's entry, but entry.get(attributeName) is null — the schema/objectClass of the entry simply has no value for the configured attribute name.

Common situations: cas.authn.pm.reset.mail/sms.attributeName points at an attribute the user entries never populate (e.g. mobile not set); attribute name typo or wrong case; entries belong to an objectClass lacking that attribute; attribute hidden by ACLs/anonymous search limits.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-pm-ldap/src/main/java/org/apereo/cas/pm/LdapPasswordManagementService.java:194

                                   final List<String> attributeNames,
                                   final List<String> ldapFilterParam) {
        return findEntries(ldapFilterParam, false)
            .keySet()
            .stream()
            .map(entry -> {
                LOGGER.debug("Found LDAP entry [{}] to use", entry);
                return attributeNames
                    .stream()
                    .map(attributeName -> SpringExpressionLanguageValueResolver.getInstance().resolve(attributeName))
                    .map(attributeName -> {
                        val attr = entry.getAttribute(attributeName);
                        if (attr != null) {
                            val attributeValue = attr.getStringValue();
                            LOGGER.debug("Found [{}] [{}] for user [{}].", attributeName,
                                attributeValue, context.getUsername());
                            return attributeValue;
                        }
                        LOGGER.warn("Could not locate LDAP attribute [{}] for [{}]",
                            attributeName, entry.getDn());
                        return null;
                    })
                    .filter(Objects::nonNull)
                    .findFirst()
                    .orElse(null);
            })
            .filter(Objects::nonNull)
            .findFirst()
            .orElse(null);
    }

    protected Map<LdapEntry, LdapPasswordManagementProperties> findEntries(
        final List<String> filterValues, final boolean transform) {
        val results = new LinkedHashMap<LdapEntry, LdapPasswordManagementProperties>();
        casProperties.getAuthn().getPm().getLdap()
            .stream()
            .sorted(Comparator.comparing(LdapPasswordManagementProperties::getName))

View on GitHub (pinned to e7288fc434)