spring-projects/spring-security · info · RememberMeAuthenticationException
No persistent token found for series id: <presentedSeries>
Error message
No persistent token found for series id: <presentedSeries>
What it means
After splitting the cookie into series and token, processAutoLoginCookie asks the token repository for the series. If the repository returns null the series is unknown (expired, purged, or never existed) and a RememberMeAuthenticationException is thrown, so the remember-me cookie is rejected and the user must log in normally.
Source
Thrown at web/src/main/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServices.java:107
* @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."));
}
if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {
throw new RememberMeAuthenticationException("Remember-me login has expired");
}
// Token also matches, so login is valid. Update the token value, keeping the
// *same* series number.
this.logger.debug(LogMessage.format("Refreshing persistent login token for user '%s', series '%s'",
token.getUsername(), token.getSeries()));
PersistentRememberMeToken newToken = new PersistentRememberMeToken(token.getUsername(), token.getSeries(),View on GitHub (pinned to 96852e8860)
Solutions
- Use JdbcTokenRepositoryImpl (or another persistent store) so series survive restarts and are shared by all nodes.
- Accept that this exception means the user must re-authenticate; ensure RememberMeAuthenticationException is handled by returning the user to the login page, not a 500.
- Increase tokenValiditySeconds or configure a cleanup policy if legitimate tokens are being purged too early.
- Verify all app instances share the same token repository and spring-security key.
Example fix
// before http.rememberMe().tokenRepository(new InMemoryTokenRepositoryImpl()); // lost on restart // after http.rememberMe().tokenRepository(new JdbcTokenRepositoryImpl(dataSource, true)); // persistent, shared
Defensive patterns
Strategy: try-catch
Validate before calling
PersistentRememberMeToken t = repo.getTokenForSeries(series);
if (t == null) {
// series unknown: redirect to login instead of attempting auto-login
} Try / catch
try {
Authentication a = rememberMeServices.autoLogin(request, response);
} catch (RememberMeAuthenticationException e) {
// cookie no longer valid: force normal login, do not treat as 500
} Prevention
- Use JdbcTokenRepositoryImpl or another persistent, shared store across nodes
- Point all app instances at the same token repository and key
- Set cleanup/retention policies longer than tokenValiditySeconds
- Warn users that server restarts with the in-memory repository log everyone out of remember-me
When it happens
Trigger: Presenting a remember-me cookie whose series id has no entry in the tokenRepository — after server restart with InMemoryTokenRepositoryImpl, after the token was deleted (password change, removeUserTokens, expiry cleanup), or against a different database/key than the one that issued the cookie.
Common situations: In-memory repository losing state on redeploy/restart while users keep old cookies; token cleanup jobs removing old series; switching between dev and prod databases; load-balanced nodes pointing at different token stores.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- Token for series '<series>' does not exist
- Remember-me login has expired
- Can not set rememberMeCookieName and custom rememberMeServic
- Could not create a default ServerMaximumSessionsExceededHand
- A ReactiveSessionRegistry is needed for concurrent session m
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/11b49453c11f9138.
Report an issue: GitHub.