apereo/cas · warning
LoggingUtils.warn(LOGGER, e)
Error message
LoggingUtils.warn(LOGGER, e)
What it means
In DuoSecurityUniversalPromptValidateLoginAction, processStateFromBrowserStorage() (invoked from handleDuoSecurityUniversalPromptResponse while validating the Duo Universal Prompt response) wraps its work in try/catch(Throwable) and only logs the failure at WARN via LoggingUtils.warn(LOGGER, e). The exception is swallowed: no error transition is returned here, and the flow continues; the 'message' value shown is the logging call itself, meaning any downstream error surfaces only in logs.
Solutions
- Reproduce with DEBUG/TRACE logging on org.apereo.cas.adaptors.duo and read the stack trace that LoggingUtils.warn emitted
- Clear browser cookies/session state and retry the Duo universal prompt flow end to end
- Verify the browser session store (e.g. session cookies / storage backend) is consistently configured across CAS nodes in a cluster
- Check for version skew between nodes that could break deserialization of stored session attributes
- If you need fail-closed behavior, override/extend the action to return the error transition instead of only logging
Example fix
// before
catch (final Throwable e) {
LoggingUtils.warn(LOGGER, e);
}
// after
catch (final Throwable e) {
LoggingUtils.error(LOGGER, e);
return eventFactory.event(this, CasWebflowConstants.TRANSITION_ID_ERROR);
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before invoking the flow action, confirm browser session store has Duo state:
var attrs = browserSessionStore.getSessionAttributes(webContext);
if (!attrs.containsKey(Credential.class.getSimpleName())) {
// restart the Duo prompt flow instead of proceeding
} Try / catch
try {
return processStateFromBrowserStorage(requestContext, browserSessionStore);
} catch (Throwable e) {
LoggingUtils.warn(LOGGER, e);
return eventFactory.event(this, CasWebflowConstants.TRANSITION_ID_ERROR);
} Prevention
- Keep session-store configuration consistent across all CAS nodes
- Avoid CAS upgrades mid-session; version-skew breaks deserialization of stored attributes
- Test the full Duo universal prompt round-trip after any session/cookie config change
- Watch WARN logs from DuoSecurityUniversalPromptValidateLoginAction in production dashboards
When it happens
Trigger: Any Throwable thrown while restoring Duo state from browser session storage and populating credential/authentication/service into the webflow context: corrupt or expired browser session store entries, deserialization failures of stored attributes, null/missing state from the Duo universal prompt round-trip.
Common situations: User's browser session/cookies expired between Duo prompt and callback; server restart wiped local session store; attributes stored in browserSessionStore fail to deserialize after a CAS upgrade; misconfigured session/cas.ticket… storage so stored attributes are absent.
Related errors
- Unknown Duo Security authentication attempt
- State [ : : ] does not have a matching transition for
- Invalid cookie . Required user-agent does not match
- Deserialization error
- Unable to locate authentication object in the webflow…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/e545da760f856eb7.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-duo-core/src/main/java/org/apereo/cas/adaptors/duo/web/flow/action/DuoSecurityUniversalPromptValidateLoginAction.java:127
browserSessionStore.getSessionAttributes(webContext).forEach((key, value) -> {
if (key.equalsIgnoreCase(FlowScope.class.getSimpleName())) {
populateRequestContextScope(value, requestContext.getFlowScope());
} else if (key.equalsIgnoreCase(FlashScope.class.getSimpleName())) {
populateRequestContextScope(value, requestContext.getFlashScope());
} else if (key.equalsIgnoreCase(RequestScope.class.getSimpleName())) {
populateRequestContextScope(value, requestContext.getRequestScope());
} else if (key.equalsIgnoreCase(ConversationScope.class.getSimpleName())) {
populateRequestContextScope(value, requestContext.getConversationScope());
} else {
requestContext.getFlowScope().put(key, value);
}
});
populateContextWithCredential(requestContext, browserSessionStore);
populateContextWithAuthentication(requestContext, browserSessionStore);
populateContextWithService(requestContext, browserSessionStore);
return super.doExecuteInternal(requestContext);
} catch (final Throwable e) {
LoggingUtils.warn(LOGGER, e);
} finally {
if (browserSessionStore != null) {
val credential = (Credential) browserSessionStore.getSessionAttributes(webContext).get(Credential.class.getSimpleName());
WebUtils.putCredential(requestContext, credential);
}
}
return eventFactory.event(this, CasWebflowConstants.TRANSITION_ID_ERROR);
}
private static JEEContext toWebContext(final RequestContext requestContext) {
val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext);
val response = WebUtils.getHttpServletResponseFromExternalWebflowContext(requestContext);
return new JEEContext(request, response);
}
private static void populateRequestContextScope(final Object flowAttributes, final MutableAttributeMap<Object> requestContext) {
val mappedAttributes = new LinkedHashMap<>((Map) flowAttributes);
CollectionUtils.filter(mappedAttributes.values(), PredicateUtils.notNullPredicate());View on GitHub (pinned to e7288fc434)