apereo/cas · error · AuthenticationException
Unable to locate authentication object in the webflow…
Error message
Unable to locate authentication object in the webflow context
What it means
DefaultCasDelegatingWebflowEventResolver.resolveInternal requires an in-progress AuthenticationResultBuilder in the webflow context before granting a TGT. If WebUtils.getAuthenticationResultBuilder(context) returns null, no authentication result was staged in this flow request, so it throws IllegalArgumentException wrapping AuthenticationException, typically surfaced as HTTP 401 via buildEventFromException handling.
Solutions
- Ensure the request goes through the standard CAS login flow so the AuthenticationResultBuilder is placed into flow scope before event resolution
- If integrating programmatically, first call WebUtils.putAuthenticationResultBuilder(context, builder) with a populated builder
- Check that authentication session management is enabled and the authn session hasn't expired mid-flow
- Clear stale flow execution state and restart the login flow
Example fix
// before: calling resolver without a builder
val events = casWebflowEventResolver.resolve(context, credential, service);
// after
val builder = WebUtils.getAuthenticationResultBuilder(context);
if (builder == null) {
builder = new DefaultAuthenticationResultBuilder(...); // or route through /login
}
WebUtils.putAuthenticationResultBuilder(context, builder);
val events = casWebflowEventResolver.resolve(context, credential, service); Defensive patterns
Strategy: type-guard
Validate before calling
if (WebUtils.getAuthenticationResultBuilder(context) == null) {
// redirect to /login instead of resolving events
} Type guard
AuthenticationResultBuilder builder = WebUtils.getAuthenticationResultBuilder(context); boolean canResolve = builder != null;
Try / catch
try {
return resolver.resolve(context, credentials, service);
} catch (IllegalArgumentException | AuthenticationException e) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return Collections.emptySet();
} Prevention
- Always route event resolution through the standard login flow
- Populate AuthenticationResultBuilder in flow scope before resolving events
- Watch for session expiry mid-flow; require fresh login when the builder is gone
When it happens
Trigger: Resolving the authentication event outside the normal login flow (e.g. a direct POST to the event resolver or a service-ticket/renew request) where WebUtils.getAuthenticationResultBuilder has never been populated; hitting the resolver mid-flow after context reset; calling resolveFrom before any credentials were submitted.
Common situations: Direct-deep-linking into a flow state; SSO/session expiry wiping the flow scope; custom webflow actions clearing request context; misconfigured flow transitions skipping the credential-submission step.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Unable to determine authentication from the request context
- Provided token is not issued by and does not belong to
- Authentication chain is empty as no authentications have…
- Adaptive authentication policy does not allow this request…
- Could not determine authentication from the request context
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/6f4f2b7cd7df4e14.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-webflow-api/src/main/java/org/apereo/cas/web/flow/resolver/impl/DefaultCasDelegatingWebflowEventResolver.java:98
val registeredService = determineRegisteredServiceForEvent(context, service);
LOGGER.trace("Attempting to resolve candidate authentication events for service [{}]", service);
val resolvedEvents = resolveCandidateAuthenticationEvents(context, service, registeredService);
if (resolvedEvents.isEmpty()) {
LOGGER.trace("No candidate authentication events were resolved for service [{}]", service);
} else {
LOGGER.trace("Authentication events resolved for [{}] are [{}]. Selecting final event...", service, resolvedEvents);
WebUtils.putResolvedEventsAsAttribute(context, resolvedEvents);
val finalResolvedEvent = this.selectiveResolver.resolveSingle(context);
LOGGER.debug("The final authentication event resolved for [{}] is [{}]", service, finalResolvedEvent);
if (finalResolvedEvent != null) {
return CollectionUtils.wrapSet(finalResolvedEvent);
}
}
val builder = WebUtils.getAuthenticationResultBuilder(context);
if (builder == null) {
val msg = "Unable to locate authentication object in the webflow context";
throw new IllegalArgumentException(new AuthenticationException(msg));
}
return CollectionUtils.wrapSet(grantTicketGrantingTicketToAuthenticationResult(context, builder, service));
} catch (final Throwable exception) {
val event = buildEventFromException(exception, context, credentials, service);
val response = WebUtils.getHttpServletResponseFromExternalWebflowContext(context);
response.setStatus(HttpStatus.UNAUTHORIZED.value());
LOGGER.debug("Authentication request failed with [{}], resulting in event [{}]", response.getStatus(), event);
return CollectionUtils.wrapSet(event);
}
}
@Override
public void addDelegate(final CasWebflowEventResolver resolver) {
if (BeanSupplier.isNotProxy(resolver)) {
orderedResolvers.add(resolver);
}
}
View on GitHub (pinned to e7288fc434)