{"record":{"id":"1cfca9d4fb3a52b6","repo":"apereo/cas","slug":"unable-to-match-required-remote-address-s-because","errorCode":null,"errorMessage":"Unable to match required remote address %s because client ip at time of cookie creation is unknown for cookie %s","messagePattern":"Unable to match required remote address (.+?) because client ip at time of cookie creation is unknown for cookie (.+?)","errorType":"exception","errorClass":"InvalidCookieException","httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-cookie-api/src/main/java/org/apereo/cas/web/support/mgmr/DefaultCasCookieValueManager.java","lineNumber":118,"sourceCode":"            LOGGER.trace(\"Cookie session-pinning is disabled for cookie [{}]. Returning cookie value as it was provided\", cookieProperties.getName());\n            return cookieValue;\n        }\n\n        if (cookieParts.size() != COOKIE_FIELDS_LENGTH) {\n            throw new InvalidCookieException(\"Invalid cookie %s. Required fields are missing\".formatted(cookieProperties.getName()));\n        }\n        val cookieClientLocationOrIp = cookieParts.get(1);\n        val cookieUserAgent = cookieParts.get(2);\n\n        if (Stream.of(cookieValue, cookieClientLocationOrIp, cookieUserAgent).anyMatch(StringUtils::isBlank)) {\n            throw new InvalidCookieException(\"Invalid cookie %s. Required fields are empty\".formatted(cookieProperties.getName()));\n        }\n\n        val clientInfo = ClientInfoHolder.getClientInfo();\n        if (clientInfo == null) {\n            val message = \"Unable to match required remote address %s because client ip at time of cookie creation is unknown for cookie %s\"\n                .formatted(cookieProperties.getName(), cookieClientLocationOrIp);\n            LOGGER.warn(message);\n            throw new InvalidCookieException(message);\n        }\n\n        if (cookieProperties.isGeoLocateClientSession()) {\n            val clientLocationOrIp = getClientGeoLocation(clientInfo);\n            if (!cookieClientLocationOrIp.equals(clientLocationOrIp)) {\n                val message = \"Invalid cookie %s Required remote address %s does not match %s\"\n                    .formatted(cookieProperties.getName(), cookieClientLocationOrIp, clientLocationOrIp);\n                LOGGER.warn(message);\n                throw new InvalidCookieException(message);\n            }\n        } else {\n            val clientIpAddress = clientInfo.getClientIpAddress();\n            if (!cookieClientLocationOrIp.equals(clientIpAddress)) {\n                if (StringUtils.isBlank(cookieProperties.getAllowedIpAddressesPattern())\n                    || !RegexUtils.find(cookieProperties.getAllowedIpAddressesPattern(), clientIpAddress)) {\n                    val message = \"Invalid cookie %s. Required remote address %s does not match %s\"\n                        .formatted(cookieProperties.getName(), cookieClientLocationOrIp, clientIpAddress);","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-cookie-api/src/main/java/org/apereo/cas/web/support/mgmr/DefaultCasCookieValueManager.java#L100-L136","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before: custom servlet path not covered by the client info filter\n// <filter-mapping><filter-name>clientInfoFilter</filter-name><url-pattern>/login</url-pattern></filter-mapping>\n// after: cover the additional path\n// <filter-mapping><filter-name>clientInfoFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>","handlingStrategy":"type-guard","validationCode":"if (ClientInfoHolder.getClientInfo() == null) {\n    throw new IllegalStateException(\"Request path not covered by client info filter\");\n}","typeGuard":"boolean clientInfoAvailable() { return ClientInfoHolder.getClientInfo() != null; }","tryCatchPattern":"try { manager.obtainCookieValue(request); } catch (InvalidCookieException e) {\n    if (ClientInfoHolder.getClientInfo() == null) { /* filter misconfiguration, not user error */ }\n}","preventionTips":["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"],"tags":["cookie","client-info","filter-chain","security"],"backgroundTag":"missing-required-context","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}