apereo/cas · critical · AuthenticationException

Resolved credentials for this transaction are empty

Error message

Resolved credentials for this transaction are empty

What it means

DefaultAuthenticationManager.authenticateInternal throws AuthenticationException when the AuthenticationTransaction contains no credentials. This indicates the credential-extraction step upstream resolved to an empty set, so there is nothing to authenticate against the handler execution plan.

Solutions

  1. Inspect the incoming request and ensure credential parameters (username/password or token) are actually submitted.
  2. Verify the credential extractors (e.g. cas.authn.accept.credentials-extractor or the flow's form object) match the request parameter names.
  3. If building the transaction programmatically, ensure you call TransactionBuilder.bind(...) with at least one credential.
  4. Check for recently changed custom extractors/filters that drop credentials; restore them or return the original credential.
  5. Enable DEBUG logging for org.apereo.cas.authentication to trace the resolved credentials list.

Example fix

// before
AuthenticationTransaction.with(service).collect(); // no credentials bound
// after
AuthenticationTransaction.with(service).bind(credential).collect();
Defensive patterns

Strategy: validation

Validate before calling

// before invoking the authentication manager
var creds = transaction.getCredentials();
if (creds == null || creds.isEmpty()) {
    throw new InvalidRequestException("No credentials bound to the authentication transaction");
}

Try / catch

try {
    return authenticationManager.authenticate(transaction);
} catch (AuthenticationException e) {
    if (e.getMessage().contains("credentials for this transaction are empty")) {
        LOGGER.error("Login request carried no credentials; check form/extractor parameter names", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Invoking the AuthenticationManager with an AuthenticationTransaction whose getCredentials() is empty — e.g. a login flow that posted no credential parameters (missing username/password fields), or a custom credential extractor that filtered out the credentials before the transaction was built.

Common situations: Webflow misconfiguration where the login form binds to different parameter names than the extractors expect; custom AuthenticationTransaction created manually with no credentials; API/REST authentication request missing body parameters; extractor beans disabled by feature conditions.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at core/cas-server-core-authentication-api/src/main/java/org/apereo/cas/authentication/DefaultAuthenticationManager.java:216

    protected Collection<AuthenticationMetaDataPopulator> getAuthenticationMetadataPopulatorsForTransaction(
        final AuthenticationTransaction transaction) {
        return authenticationEventExecutionPlan.getAuthenticationMetadataPopulators(transaction);
    }

    protected void publishEvent(final ApplicationEvent event) {
        if (applicationContext != null) {
            applicationContext.publishEvent(event);
        }
    }

    protected AuthenticationBuilder authenticateInternal(final AuthenticationTransaction transaction) throws Throwable {
        val credentials = transaction.getCredentials();
        LOGGER.debug("Authentication credentials provided for this transaction are [{}]", credentials);

        if (credentials.isEmpty()) {
            LOGGER.error("Resolved authentication handlers for this transaction are empty");
            throw new AuthenticationException("Resolved credentials for this transaction are empty");
        }

        val authenticationBuilder = new DefaultAuthenticationBuilder(NullPrincipal.getInstance());
        credentials.forEach(authenticationBuilder::addCredential);

        val handlerSet = authenticationEventExecutionPlan.resolveAuthenticationHandlers(transaction);
        LOGGER.debug("Candidate resolved authentication handlers for this transaction are [{}]", handlerSet);

        try {
            for (val credential : credentials) {
                LOGGER.debug("Attempting to authenticate credential [{}]", credential);

                val itHandlers = handlerSet.iterator();
                var proceedWithNextHandler = true;
                while (proceedWithNextHandler && itHandlers.hasNext()) {
                    val handler = itHandlers.next();
                    if (handler.supports(credential)) {
                        try {

View on GitHub (pinned to e7288fc434)