apereo/cas · warning · InvalidCookieException
Unable to match required remote address
Error message
Unable to match required remote address %s because client ip at time of cookie creation is unknown for cookie %s
What it means
When reading a compound cookie, CAS compares the client IP/location stored at cookie creation against the current ClientInfoHolder client info. If no ClientInfo is bound to the thread (the request never populated it), the match cannot be performed and InvalidCookieException is thrown. This is a safety check so cookies are never accepted without proving client binding.
Solutions
- Register/enable the CAS client info filter so ClientInfoHolder is populated for the request path in question
- Move cookie validation after the client info filter in the filter chain
- Check the servlet mapping/filter mapping covers the endpoint hitting this code
- If invoked outside a web request, provide a ClientInfo via ClientInfoHolder.setClientInfo(...) around the call or refactor to avoid cookie validation there
Example fix
// before: custom servlet path not covered by the client info filter // <filter-mapping><filter-name>clientInfoFilter</filter-name><url-pattern>/login</url-pattern></filter-mapping> // after: cover the additional path // <filter-mapping><filter-name>clientInfoFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
Defensive patterns
Strategy: type-guard
Validate before calling
if (ClientInfoHolder.getClientInfo() == null) {
throw new IllegalStateException("Request path not covered by client info filter");
} Type guard
boolean clientInfoAvailable() { return ClientInfoHolder.getClientInfo() != null; } Try / catch
try { manager.obtainCookieValue(request); } catch (InvalidCookieException e) {
if (ClientInfoHolder.getClientInfo() == null) { /* filter misconfiguration, not user error */ }
} Prevention
- Map the client info filter to /* so all cookie-validating paths have context
- Never call cookie validation from threads without a bound ClientInfo
- Set ClientInfoHolder explicitly in tests/scheduled jobs
When it happens
Trigger: obtainValueFromCompoundCookie runs but ClientInfoHolder.getClientInfo() returns null — the request was not processed by the ClientInfo filter/interceptor (e.g. a custom filter chain, actuator endpoint, async servlet path, or direct servlet call that skips CAS's client info filter).
Common situations: Developer added a filter that consumes the ticket-granting cookie on paths not covered by ClientInfoThreadLocalFilter; calling cookie manager from a non-web context (scheduled job, websocket); misordered filter chain where cookie validation happens before client info is captured.
Related errors
- Invalid cookie . Required user-agent does not match
- Invalid cookie . Required fields are empty
- Invalid cookie Required remote address does not match
- Invalid cookie . Required remote address does not match
- Invalid cookie . Required user-agent does not match
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/1cfca9d4fb3a52b6.
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:118
LOGGER.trace("Cookie session-pinning is disabled for cookie [{}]. Returning cookie value as it was provided", cookieProperties.getName());
return cookieValue;
}
if (cookieParts.size() != COOKIE_FIELDS_LENGTH) {
throw new InvalidCookieException("Invalid cookie %s. Required fields are missing".formatted(cookieProperties.getName()));
}
val cookieClientLocationOrIp = cookieParts.get(1);
val cookieUserAgent = cookieParts.get(2);
if (Stream.of(cookieValue, cookieClientLocationOrIp, cookieUserAgent).anyMatch(StringUtils::isBlank)) {
throw new InvalidCookieException("Invalid cookie %s. Required fields are empty".formatted(cookieProperties.getName()));
}
val clientInfo = ClientInfoHolder.getClientInfo();
if (clientInfo == null) {
val message = "Unable to match required remote address %s because client ip at time of cookie creation is unknown for cookie %s"
.formatted(cookieProperties.getName(), cookieClientLocationOrIp);
LOGGER.warn(message);
throw new InvalidCookieException(message);
}
if (cookieProperties.isGeoLocateClientSession()) {
val clientLocationOrIp = getClientGeoLocation(clientInfo);
if (!cookieClientLocationOrIp.equals(clientLocationOrIp)) {
val message = "Invalid cookie %s Required remote address %s does not match %s"
.formatted(cookieProperties.getName(), cookieClientLocationOrIp, clientLocationOrIp);
LOGGER.warn(message);
throw new InvalidCookieException(message);
}
} else {
val clientIpAddress = clientInfo.getClientIpAddress();
if (!cookieClientLocationOrIp.equals(clientIpAddress)) {
if (StringUtils.isBlank(cookieProperties.getAllowedIpAddressesPattern())
|| !RegexUtils.find(cookieProperties.getAllowedIpAddressesPattern(), clientIpAddress)) {
val message = "Invalid cookie %s. Required remote address %s does not match %s"
.formatted(cookieProperties.getName(), cookieClientLocationOrIp, clientIpAddress);View on GitHub (pinned to e7288fc434)