spring-projects/spring-security · error · InvalidCookieException

Cookie token[1] did not contain a valid number (contained…

Error message

Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1] + "')

What it means

The second token in the remember-me cookie must parse as a long (epoch millis expiry). getTokenExpiryTime wraps Long.valueOf(cookieTokens[1]) and converts any NumberFormatException into InvalidCookieException reporting the offending value. It means the cookie is malformed at the expiry position.

Solutions

  1. Clear the cookie and log in again
  2. Ensure no two apps on the same domain use the same remember-me cookie name
  3. Verify intermediaries are not truncating or rewriting the Cookie header
  4. After format upgrades, clear legacy cookies

Example fix

// before (colliding cookie names on shared domain)
http.rememberMe(r -> r.key("k"));
// after
http.rememberMe(r -> r.key("k").cookieName("myapp-remember-me"));
Defensive patterns

Strategy: try-catch

Validate before calling

String[] parts = cookieValue.split(":");
if (parts.length < 2 || !parts[1].matches("\\d+")) {
    deleteRememberMeCookie(response);
    return;
}

Type guard

boolean hasNumericExpiry(String[] cookieTokens) {
    return cookieTokens.length >= 2 && cookieTokens[1].matches("\\d+");
}

Try / catch

try {
    UserDetails u = rememberMeServices.autoLogin(request, response);
} catch (InvalidCookieException e) {
    cookieClearingLogoutHandler.logout(request, response, null);
    response.sendRedirect("/login");
}

Prevention

When it happens

Trigger: processAutoLoginCookie calls getTokenExpiryTime with a cookie whose second colon-separated token is non-numeric — corrupted cookie, truncation, wrong delimiter, or a cookie produced by an incompatible format.

Common situations: Cookie mangled by proxies or browser extensions; apps sharing cookie name with different formats; manual editing or debugging of cookies; partial cookie writes.

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


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/d10c9c6a98775518. Report an issue: GitHub.

Appendix: source

Thrown at web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java:174

		String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails.getUsername(),
				userDetails.getPassword(), actualAlgorithm);
		if (!equals(expectedTokenSignature, actualTokenSignature)) {
			throw new InvalidCookieException("Cookie contained signature '" + actualTokenSignature + "' but expected '"
					+ expectedTokenSignature + "'");
		}
		return userDetails;
	}

	private boolean isValidCookieTokensLength(String[] cookieTokens) {
		return cookieTokens.length == 3 || cookieTokens.length == 4;
	}

	private long getTokenExpiryTime(String[] cookieTokens) {
		try {
			return Long.valueOf(cookieTokens[1]);
		}
		catch (NumberFormatException nfe) {
			throw new InvalidCookieException(
					"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1] + "')");
		}
	}

	/**
	 * Calculates the digital signature to be put in the cookie. Default value is
	 * {@link #encodingAlgorithm} applied to ("username:tokenExpiryTime:password:key")
	 */
	protected String makeTokenSignature(long tokenExpiryTime, String username, String password) {
		String data = username + ":" + tokenExpiryTime + ":" + password + ":" + getKey();
		try {
			MessageDigest digest = MessageDigest.getInstance(this.encodingAlgorithm.getDigestAlgorithm());
			return new String(Hex.encode(digest.digest(data.getBytes())));
		}
		catch (NoSuchAlgorithmException ex) {
			throw new IllegalStateException("No " + this.encodingAlgorithm.name() + " algorithm available!");
		}
	}

View on GitHub (pinned to 96852e8860)