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
- Ensure clients always send a User-Agent header (set one in curl/http clients, e.g. curl -A 'MyAgent/1.0').
- Check intermediary proxies/WAFs and re-add the User-Agent header if stripped.
- If the client is a non-browser integration, disable session pinning (e.g. cas.tgc.pin-to-user-agent=false).
- 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
- Always send User-Agent from scripts, integrations and health probes
- Verify proxies/WAFs do not strip User-Agent
- Disable pin-to-user-agent for API-only integrations
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
- Invalid cookie . Required user-agent does not match
- Invalid cookie . Required user-agent does not match
- Cookie name is undefined
- Unable to accept cookie for authentication
- No state could be found to determine session state
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)