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

  1. Register/enable the CAS client info filter so ClientInfoHolder is populated for the request path in question
  2. Move cookie validation after the client info filter in the filter chain
  3. Check the servlet mapping/filter mapping covers the endpoint hitting this code
  4. 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

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


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)