apereo/cas · error · IllegalStateException

Request does not specify a user-agent

Error message

Request does not specify a user-agent

What it means

DefaultCasCookieValueManager.buildCompoundCookieValue builds a pinned cookie value combining principal, IP/geo location, and the request's User-Agent when cookie session pinning is enabled. If HttpRequestUtils.getHttpServletRequestUserAgent(request) yields a blank user agent, it throws IllegalStateException because the pinned value could never be verified on later requests.

Solutions

  1. Ensure clients always send a User-Agent header (set one in curl/http clients, e.g. curl -A 'MyAgent/1.0').
  2. Check intermediary proxies/WAFs and re-add the User-Agent header if stripped.
  3. If the client is a non-browser integration, disable session pinning (e.g. cas.tgc.pin-to-user-agent=false).
  4. Exclude health-check/probe paths from cookie-based authenticated flows.

Example fix

// before
curl -X POST https://cas.example.org/cas/login -d username=u -d password=p
// after
curl -A "svc-client/1.0" -X POST https://cas.example.org/cas/login -d username=u -d password=p
Defensive patterns

Strategy: validation

Validate before calling

if (HttpRequestUtils.getHttpServletRequestUserAgent(request) == null) {
  // do not call buildCompoundCookieValue with pinning enabled
}

Try / catch

try {
  val value = cookieManager.buildCompoundCookieValue(...);
} catch (IllegalStateException e) {
  LOGGER.warn("Missing user-agent for pinned cookie", e);
}

Prevention

When it happens

Trigger: Calling buildCompoundCookieValue (via buildValue when encrypt/decrypt + pinning are enabled, e.g. for the TGC) with an HttpServletRequest that has no User-Agent header while cas.tgc.pin-to-user-agent (or similar pinning flag) is true.

Common situations: API/non-browser clients, health checks, or curl scripts calling CAS login endpoints without a User-Agent header; corporate proxies or WAFs stripping the header; load tests / http client defaults omitting the header; container probes hitting authenticated endpoints.

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/c2c65392fc6bb82e. Report an issue: GitHub.

Appendix: source

Thrown at core/cas-server-core-cookie-api/src/main/java/org/apereo/cas/web/support/mgmr/DefaultCasCookieValueManager.java:69

        this.geoLocationService = geoLocationService;
        this.cookieProperties = cookieProperties;
    }

    @Override
    protected String buildCompoundCookieValue(final String givenCookieValue, final HttpServletRequest request) {
        val builder = new StringBuilder(givenCookieValue);

        if (cookieProperties.isPinToSession()) {
            val clientInfo = ClientInfoHolder.getClientInfo();
            if (clientInfo != null) {
                val clientLocation = cookieProperties.isGeoLocateClientSession()
                    ? getClientGeoLocation(clientInfo)
                    : clientInfo.getClientIpAddress();
                builder.append(COOKIE_FIELD_SEPARATOR).append(clientLocation);
            }
            val userAgent = HttpRequestUtils.getHttpServletRequestUserAgent(request);
            if (StringUtils.isBlank(userAgent)) {
                throw new IllegalStateException("Request does not specify a user-agent");
            }
            builder.append(COOKIE_FIELD_SEPARATOR).append(userAgent);
        } else {
            LOGGER.trace("Cookie session-pinning is disabled");
        }

        return builder.toString();
    }

    private String getClientGeoLocation(final ClientInfo clientInfo) {
        return geoLocationService
            .stream()
            .map(service -> {
                val geoLocation = service.locate(clientInfo.getClientIpAddress());
                if (geoLocation != null && geoLocation.getAddresses() != null && !geoLocation.getAddresses().isEmpty()) {
                    return org.springframework.util.StringUtils.collectionToCommaDelimitedString(geoLocation.getAddresses());
                }
                return clientInfo.getClientIpAddress();

View on GitHub (pinned to e7288fc434)