spring-projects/spring-security · error · InvalidCookieException
Cookie token did not contain 3 or 4 tokens, but contained '[
Error message
Cookie token did not contain 3 or 4 tokens, but contained '[cookieTokens]'
What it means
TokenBasedRememberMeServices expects the remember-me cookie to decode into exactly 3 tokens (username:expiry:signature) or 4 when an algorithm is included (username:expiry:algorithm:signature). If the decoded cookie splits into any other number of parts, InvalidCookieException is thrown with the actual token list. This guards against tampered, truncated, or foreign cookies.
Source
Thrown at web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java:129
* Construct the instance with the parameters provided.
* @param key the signature key
* @param userDetailsService the {@link UserDetailsService}
* @param encodingAlgorithm the {@link RememberMeTokenAlgorithm} used to encode the
* signature
* @since 5.8
*/
public TokenBasedRememberMeServices(String key, UserDetailsService userDetailsService,
RememberMeTokenAlgorithm encodingAlgorithm) {
super(key, userDetailsService);
Assert.notNull(encodingAlgorithm, "encodingAlgorithm cannot be null");
this.encodingAlgorithm = encodingAlgorithm;
}
@Override
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
HttpServletResponse response) {
if (!isValidCookieTokensLength(cookieTokens)) {
throw new InvalidCookieException(
"Cookie token did not contain 3 or 4 tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
}
long tokenExpiryTime = getTokenExpiryTime(cookieTokens);
if (isTokenExpired(tokenExpiryTime)) {
throw new InvalidCookieException("Cookie token[1] has expired (expired on '" + new Date(tokenExpiryTime)
+ "'; current time is '" + new Date() + "')");
}
// Check the user exists. Defer lookup until after expiry time checked, to
// possibly avoid expensive database call.
UserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);
Assert.notNull(userDetails, () -> "UserDetailsService " + getUserDetailsService()
+ " returned null for username " + cookieTokens[0] + ". " + "This is an interface contract violation");
// Check signature of token matches remaining details. Must do this after user
// lookup, as we need the DAO-derived password. If efficiency was a major issue,
// just add in a UserCache implementation, but recall that this method is usually
// only called once per HttpSession - if the token is valid, it will cause
// SecurityContextHolder population, whilst if invalid, will cause the cookie to
// be cancelled.View on GitHub (pinned to 96852e8860)
Solutions
- Log the user in again to issue a fresh, correctly formatted cookie
- Ensure cookie names (and the key) don't collide between apps on the same domain
- Clear old cookies after upgrading Spring Security versions that changed the cookie format
- Check intermediaries (proxies, WAFs) are not truncating the Set-Cookie/Cookie headers
Example fix
// before (apps sharing cookie)
http.rememberMe(r -> r.key("shared-key")); // both apps, cookie 'remember-me' collides
// after
http.rememberMe(r -> r.rememberMeParameter("remember-me").key("app1-key")
.cookieName("app1-remember-me")); Defensive patterns
Strategy: validation
Validate before calling
String[] parts = cookieValue.split(":");
if (parts.length != 3 && parts.length != 4) {
// drop the cookie and redirect to login before invoking the filter
deleteRememberMeCookie(response);
return;
} Type guard
boolean isValidRememberMeCookie(String v) {
if (v == null) return false;
int n = v.split(":", -1).length;
return n == 3 || n == 4;
} Try / catch
try {
UserDetails u = rememberMeServices.autoLogin(request, response);
} catch (InvalidCookieException e) {
cookieClearingLogoutHandler.logout(request, response, null);
chain.doFilter(request, response);
} Prevention
- Use unique remember-me cookie names per app on shared domains
- Clear legacy cookies when upgrading Spring Security versions
- Test cookie handling behind proxies/CDNs
- Never manually construct remember-me cookies
When it happens
Trigger: processAutoLoginCookie receives a cookieTokens array whose length is not 3 or 4 — typically because the cookie value was corrupted, truncated by a proxy, encoded with a different delimiter, or produced by another application sharing the cookie name/key.
Common situations: Multiple apps on the same domain sharing the remember-me cookie name; cookie mangled by reverse proxy or CDN; version change in cookie format (e.g. 4-token format introduced for algorithm support) mixing old and new cookies; manual cookie editing.
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 2 tokens, but contained '[cooki
- Autologin failed due to data access problem
- Cookie token[1] has expired (expired on '<expiryDate>'; curr
- Cookie token[1] did not contain a valid number (contained '"
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/ba1ed18f7a5391af.
Report an issue: GitHub.