spring-projects/spring-security · error · IllegalArgumentException

Expected number but found

Error message

Expected number but found {tokens[0]}

What it means

KeyBasedPersistenceTokenService.verifyToken parses the token's ':'-separated fields and expects the first field to be a number (creation time, parsed with Long.decode). If parsing fails the token is malformed or tampered, and it throws IllegalArgumentException.

Solutions

  1. Regenerate the token by calling allocateToken again and have the user re-authenticate
  2. Verify the token was not truncated or re-encoded in transit/storage (check URL-encoding, cookie handling)
  3. Wrap verifyToken in try-catch (IllegalArgumentException) and treat failures as 'token invalid' rather than crashing

Example fix

// before
Token token = service.verifyToken(rawToken);
// after
try {
    Token token = service.verifyToken(rawToken);
} catch (IllegalArgumentException ex) {
    // invalid token: force re-authentication
    service.allocateToken(user.getKey());
}
Defensive patterns

Strategy: try-catch

Validate before calling

String[] tokens = rawToken.split(":"); boolean looksNumeric = tokens.length >= 4 && tokens[0].matches("(0[xX])?[0-9a-fA-F]+|[+-]?[0-9]+");

Type guard

boolean plausiblyValidToken(String t) { return t != null && t.split(":").length >= 4; }

Try / catch

try { token = service.verifyToken(rawToken); } catch (IllegalArgumentException ex) { token = null; // treat as invalid: force re-auth }

Prevention

When it happens

Trigger: Calling verifyToken with a token whose first field is not a valid number — corrupted token, truncated token, a token produced by a different service/version, or manual construction of the token string.

Common situations: Remember-me / persistent tokens mangled by URL encoding or storage truncation; tampering attempts (tests verify with tampered keys); switching the secret key invalidates nothing here, but a hand-edited token does.

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/23e2be438a45bb1d. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/springframework/security/core/token/KeyBasedPersistenceTokenService.java:119

		String sha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
		String keyPayload = content + ":" + sha512Hex;
		return Utf8.decode(Base64.getEncoder().encode(Utf8.encode(keyPayload)));
	}

	@Override
	public @Nullable Token verifyToken(String key) {
		if (key == null || "".equals(key)) {
			return null;
		}
		String[] tokens = StringUtils
			.delimitedListToStringArray(Utf8.decode(Base64.getDecoder().decode(Utf8.encode(key))), ":");
		Assert.isTrue(tokens.length >= 4, () -> "Expected 4 or more tokens but found " + tokens.length);
		long creationTime;
		try {
			creationTime = Long.decode(tokens[0]);
		}
		catch (NumberFormatException ex) {
			throw new IllegalArgumentException("Expected number but found " + tokens[0]);
		}
		String serverSecret = computeServerSecretApplicableAt(creationTime);
		String pseudoRandomNumber = tokens[1];
		// Permit extendedInfo to itself contain ":" characters
		StringBuilder extendedInfo = new StringBuilder();
		for (int i = 2; i < tokens.length - 1; i++) {
			if (i > 2) {
				extendedInfo.append(":");
			}
			extendedInfo.append(tokens[i]);
		}
		String sha1Hex = tokens[tokens.length - 1];
		// Verification
		String content = creationTime + ":" + pseudoRandomNumber + ":" + extendedInfo.toString();
		String expectedSha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
		Assert.isTrue(Utf8.isEqual(expectedSha512Hex, sha1Hex), "Key verification failure");
		return new DefaultToken(key, creationTime, extendedInfo.toString());
	}

View on GitHub (pinned to 96852e8860)