apereo/cas · error · IllegalArgumentException

No state could be found to determine session state

Error message

No state could be found to determine session state

What it means

WsFederationCookieManager.retrieve() reconstructs the delegated-authentication session state from a serialized 'serverState' value taken from the request session (keyed by the identity-provider configuration id). If no non-blank state value can be found, CAS cannot correlate the callback with an in-flight WS-Federation sign-in and throws IllegalArgumentException to abort the flow.

Solutions

  1. Ensure the CAS session cookie survives the full WS-Federation round trip (SameSite settings, no cookie stripping, sticky sessions on the load balancer).
  2. Restart the delegated sign-in from the initial /login redirect instead of invoking the callback URL directly.
  3. Verify the WsFederation identity-provider configuration id is stable across nodes and restarts, since it names the state attribute/cookie.
  4. Increase session timeout or persist state out-of-session if IdP redirects are slow.

Example fix

// before
String serverState = cookieManager.retrieve(request);
// after
try {
    String serverState = cookieManager.retrieve(request);
} catch (IllegalArgumentException e) {
    LOGGER.warn("WS-Fed state missing; restarting delegated authentication", e);
    response.sendRedirect(loginUrl);
}
Defensive patterns

Strategy: try-catch

Validate before calling

HttpSession s = request.getSession(false);
Object st = (s != null) ? s.getAttribute(config.getId()) : null;
if (!(st instanceof String) || ((String) st).isBlank()) { /* restart delegated auth before calling retrieve */ }

Type guard

boolean hasServerState(HttpServletRequest req, WsFederationIdentityProviderConfiguration cfg) {
    HttpSession s = req.getSession(false);
    return s != null && s.getAttribute(cfg.getId()) instanceof String st && !st.isBlank();
}

Try / catch

try {
    cookieManager.retrieve(request);
} catch (IllegalArgumentException e) {
    // missing/blank WS-Fed state: restart the delegated sign-in redirect
}

Prevention

When it happens

Trigger: retrieve() is invoked during WS-Federation callback handling but neither the session attribute named configuration.getId() nor the state cookie yields a non-blank string — e.g. the user hits the callback URL directly, the HTTP session expired during the IdP redirect, or cookies were dropped.

Common situations: Browsers blocking cookies (SameSite/third-party), load-balanced CAS without sticky sessions, session timeout between /login redirect and ADFS callback, users bookmarking or replaying callback URLs, or a changed configuration id making the state key unresolvable.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-wsfederation/src/main/java/org/apereo/cas/support/wsfederation/web/WsFederationCookieManager.java:70

            throw new IllegalArgumentException("No " + WCTX + " parameter is found");
        }

        val configuration = configurations.stream()
            .filter(cookie -> cookie.getId().equalsIgnoreCase(contextId))
            .findFirst()
            .orElseThrow(() -> new IllegalArgumentException("Could not locate WsFederation configuration for " + contextId));

        val cookieGen = configuration.getCookieGenerator();
        var serverState = cookieGen.retrieveCookieValue(request);
        if (StringUtils.isBlank(serverState)) {
            serverState = Optional.ofNullable(request.getSession(false))
                .map(session -> session.getAttribute(configuration.getId()))
                .map(String.class::cast)
                .orElse(null);
        }
        if (StringUtils.isBlank(serverState)) {
            LOGGER.error("No server state value could be retrieved to determine the state of the delegated authentication session");
            throw new IllegalArgumentException("No state could be found to determine session state");
        }
        val blob = EncodingUtils.hexDecode(serverState);
        val session = serializer.from(blob);
        request.setAttribute(casProperties.getTheme().getParamName(), session.get(casProperties.getTheme().getParamName()));
        request.setAttribute(casProperties.getLocale().getParamName(), session.get(casProperties.getLocale().getParamName()));
        request.setAttribute(CasProtocolConstants.PARAMETER_METHOD, session.get(CasProtocolConstants.PARAMETER_METHOD));

        val serviceKey = CasProtocolConstants.PARAMETER_SERVICE + '-' + contextId;
        val service = (Service) session.get(serviceKey);
        LOGGER.debug("Located service [{}] from session", service);
        WebUtils.putServiceIntoFlowScope(context, service);
        return service;
    }

    /**
     * Store.
     *
     * @param request       the request

View on GitHub (pinned to e7288fc434)