apereo/cas · warning · InvalidCookieException

Invalid cookie . Required fields are empty

Error message

Invalid cookie %s. Required fields are empty

What it means

DefaultCasCookieValueManager builds compound cookie values (value, client location/ip, user-agent) and validates each part when reading the cookie back. InvalidCookieException is thrown when any required field in the decoded compound cookie is blank, meaning the cookie was corrupted, truncated, or forged. This protects CAS from accepting cookies missing binding data used for anti-cloning checks.

Solutions

  1. Clear the browser cookie and re-authenticate; stale/malformed cookies are simply rejected
  2. Verify cas.tgc.crypto signing/encryption keys are identical across all CAS nodes in the cluster
  3. Ensure all CAS nodes run the same CAS version so the compound cookie format matches
  4. Check for proxies/filters modifying the Cookie header; inspect the raw cookie contents
  5. If cookies are consistently rejected, disable pinnable/compound cookie fields you do not need (e.g. disable client binding checks so fewer fields are required)

Example fix

// before: keys differ per node causing undecodable cookies
// cas.tgc.crypto.encryption.key=AAA...
// cas.tgc.crypto.encryption.key=BBB... (node 2)
// after: same keys everywhere
// cas.tgc.crypto.encryption.key=AAA... (all nodes)
// cas.tgc.crypto.signing.key=CCC... (all nodes)
Defensive patterns

Strategy: validation

Validate before calling

String[] parts = compoundValue.split(":");
if (parts.length < 3 || Stream.of(parts).anyMatch(StringUtils::isBlank)) {
    // fail fast: clear cookie and force re-authentication
}

Type guard

boolean hasRequiredFields(String[] parts) {
    return parts != null && parts.length >= 3 && Stream.of(parts).noneMatch(StringUtils::isBlank);
}

Try / catch

try { manager.obtainCookieValue(...); } catch (InvalidCookieException e) {
    LOGGER.warn("Rejecting cookie", e);
    cookieGrantingCookieBuilder.remove(); // clear and re-authenticate
}

Prevention

When it happens

Trigger: obtainValueFromCompoundCookie splits the decrypted compound cookie value and any of the parts (value, client location/ip, user-agent) is blank — typically after manual cookie editing, partial cookie writes, encryption-key changes causing garbage decode, or cookies created by a different CAS version with a different compound format.

Common situations: Developer changed cas.tgc.crypto.encryption.key/signing.key between deployments so old cookies decode to malformed strings; user copied a TGC cookie from another browser or machine; a proxy or custom filter rewrote/truncated the Cookie header; testing with hand-crafted cookies.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/2902b77cc54b56ba. 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:111

    @Override
    protected String obtainValueFromCompoundCookie(final String value, final HttpServletRequest request) {
        val cookieParts = Splitter.on(String.valueOf(COOKIE_FIELD_SEPARATOR)).splitToList(value);

        val cookieValue = cookieParts.getFirst();
        if (!cookieProperties.isPinToSession()) {
            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);
            }

View on GitHub (pinned to e7288fc434)