spring-projects/spring-security · warning · InvalidCookieException
Cookie token did not contain 2 tokens, but contained '[cooki
Error message
Cookie token did not contain 2 tokens, but contained '[cookieTokens]'
What it means
PersistentTokenBasedRememberMeServices.processAutoLoginCookie expects the decoded remember-me cookie to contain exactly two tokens: the series id and the token value. A cookie yielding any other number of tokens is rejected with InvalidCookieException because its structure is invalid.
Source
Thrown at web/src/main/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServices.java:99
}
/**
* Locates the presented cookie data in the token repository, using the series id. If
* the data compares successfully with that in the persistent store, a new token is
* generated and stored with the same series. The corresponding cookie value is set on
* the response.
* @param cookieTokens the series and token values
* @throws RememberMeAuthenticationException if there is no stored token corresponding
* to the submitted cookie, or if the token in the persistent store has expired.
* @throws InvalidCookieException if the cookie doesn't have two tokens as expected.
* @throws CookieTheftException if a presented series value is found, but the stored
* token is different from the one presented.
*/
@Override
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
HttpServletResponse response) {
if (cookieTokens.length != 2) {
throw new InvalidCookieException("Cookie token did not contain " + 2 + " tokens, but contained '"
+ Arrays.asList(cookieTokens) + "'");
}
String presentedSeries = cookieTokens[0];
String presentedToken = cookieTokens[1];
PersistentRememberMeToken token = this.tokenRepository.getTokenForSeries(presentedSeries);
if (token == null) {
// No series match, so we can't authenticate using this cookie
throw new RememberMeAuthenticationException("No persistent token found for series id: " + presentedSeries);
}
// We have a match for this user/series combination
if (!presentedToken.equals(token.getTokenValue())) {
// Token doesn't match series value. Delete all logins for this user and throw
// an exception to warn them.
this.tokenRepository.removeUserTokens(token.getUsername());
throw new CookieTheftException(this.messages.getMessage(
"PersistentTokenBasedRememberMeServices.cookieStolen",
"Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
}View on GitHub (pinned to 96852e8860)
Solutions
- Clear the stale cookie (cancelCookie happens automatically) and log in again so a fresh, well-formed cookie is issued.
- Ensure usernames and token values never contain the ':' delimiter; sanitize or restrict usernames at registration.
- Do not override encodeCookie/decodeCookie in ways that change the token count.
- If migrating from another remember-me format, invalidate all existing cookies (change the key) so old cookies are rejected cleanly.
Example fix
// before
// username "alice:admin" -> cookie decodes to ["alice","admin",token] -> 3 tokens
usernameValidator.validateUsername("alice:admin"); // not enforced
// after
Assert.isTrue(!username.contains(":"), "username must not contain ':'"); // keep cookie 2-token format Defensive patterns
Strategy: try-catch
Validate before calling
String plain = new String(Base64.getDecoder().decode(cookieValue), StandardCharsets.UTF_8);
if (plain.split(":", -1).length != 2) {
// malformed remember-me cookie: cancel it
} Try / catch
try {
Authentication a = rememberMeServices.autoLogin(request, response);
} catch (InvalidCookieException e) {
((AbstractRememberMeServices) rememberMeServices).cancelCookie(request, response);
} Prevention
- Reject usernames containing ':' at registration/validation time
- Change the remember-me key when migrating cookie formats to invalidate old cookies
- Do not override encodeCookie/decodeCookie to alter the token count
- Clear cookies after upgrade migrations rather than letting old formats fail
When it happens
Trigger: autoLogin -> processAutoLoginCookie receiving a cookieTokens array whose length != 2 — caused by a corrupted cookie, a cookie whose embedded values contain the ':' delimiter (which is also the token separator), or extra/missing fields from a custom encodeCookie override.
Common situations: Usernames containing ':' splitting into three tokens after decode; old-format cookies from a different remember-me implementation after a migration; manual cookie editing; decoding producing extra elements due to unescaped delimiters.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Cookie token was not Base64 encoded; value was '<cookieValue
- Cookie token did not contain 3 or 4 tokens, but contained '[
- Cookie token[1] has expired (expired on '<expiryDate>'; curr
- Cookie token[1] did not contain a valid number (contained '"
- Can not set rememberMeCookieName and custom rememberMeServic
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/22b32e7517218a61.
Report an issue: GitHub.