apereo/cas · error · IllegalArgumentException

No wctx parameter is found

Error message

No wctx parameter is found

What it means

Thrown by WsFederationCookieManager.retrieve when the incoming WS-Federation request has no wctx parameter (or it is blank). The wctx value is used to select the matching WsFederationConfiguration (by id) from the registered configurations, so without it the request cannot be routed to an identity provider configuration.

Solutions

  1. Verify the IdP echoes the wctx parameter back on the sign-in response; check the relying-party trust configuration preserves it.
  2. Ensure clients initiate the flow via CAS's WS-Federation entry point (which sets wctx) rather than hitting the callback URL directly.
  3. Check reverse proxies/load balancers are not stripping query parameters on the redirect back to CAS.
  4. If you construct callback URLs yourself in tests or integrations, include the original wctx value from the outgoing request.

Example fix

// before: callback invoked without wctx
String callback = "https://cas.example.org/cas/login?client_name=WSFederation";
// after: preserve wctx from the original redirect
String callback = "https://cas.example.org/cas/login?client_name=WSFederation&wctx=" + originalWctx;
Defensive patterns

Strategy: validation

Validate before calling

// guard before entering the validation flow
String wctx = request.getParameter("wctx");
if (wctx == null || wctx.isBlank()) {
    throw new IllegalArgumentException("wctx parameter missing; requests must originate from the CAS WS-Federation entry point");
}

Type guard

function hasWctx(request) {
  const wctx = request.query && request.query.wctx;
  return typeof wctx === 'string' && wctx.length > 0;
}

Try / catch

try {
    wsFederationCookieManager.retrieve(context);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("wctx")) {
        LOGGER.warn("Request reached WS-Fed validation without wctx; redirecting to entry point");
        return redirectToWsFederationEntryPoint();
    }
    throw e;
}

Prevention

When it happens

Trigger: A request reaches the WS-Federation callback/validation flow without a wctx request parameter: the IdP did not echo back wctx, the client dropped the query parameter, or the request bypassed the initial CAS WS-Federation redirect that establishes it.

Common situations: Users bookmarking or hand-crafting the callback URL without wctx, a misconfigured IdP relying-party trust that strips custom query parameters, proxies/rewrites dropping query strings, or testing the callback endpoint directly.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

    private final Collection<WsFederationConfiguration> configurations;
    private final CasConfigurationProperties casProperties;

    private final WsFederationServerStateSerializer serializer;

    /**
     * Retrieve service.
     *
     * @param context the request context
     * @return the service
     */
    public Service retrieve(final RequestContext context) {
        val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context);

        val contextId = request.getParameter(WCTX);
        LOGGER.debug("Parameter [{}] received: [{}]", WCTX, contextId);
        if (StringUtils.isBlank(contextId)) {
            LOGGER.error("No [{}] parameter is found", WCTX);
            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");

View on GitHub (pinned to e7288fc434)