{"record":{"id":"23e2be438a45bb1d","repo":"spring-projects/spring-security","slug":"expected-number-but-found-tokens-0","errorCode":null,"errorMessage":"Expected number but found {tokens[0]}","messagePattern":"Expected number but found (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/springframework/security/core/token/KeyBasedPersistenceTokenService.java","lineNumber":119,"sourceCode":"\t\tString sha512Hex = Sha512DigestUtils.shaHex(content + \":\" + serverSecret);\n\t\tString keyPayload = content + \":\" + sha512Hex;\n\t\treturn Utf8.decode(Base64.getEncoder().encode(Utf8.encode(keyPayload)));\n\t}\n\n\t@Override\n\tpublic @Nullable Token verifyToken(String key) {\n\t\tif (key == null || \"\".equals(key)) {\n\t\t\treturn null;\n\t\t}\n\t\tString[] tokens = StringUtils\n\t\t\t.delimitedListToStringArray(Utf8.decode(Base64.getDecoder().decode(Utf8.encode(key))), \":\");\n\t\tAssert.isTrue(tokens.length >= 4, () -> \"Expected 4 or more tokens but found \" + tokens.length);\n\t\tlong creationTime;\n\t\ttry {\n\t\t\tcreationTime = Long.decode(tokens[0]);\n\t\t}\n\t\tcatch (NumberFormatException ex) {\n\t\t\tthrow new IllegalArgumentException(\"Expected number but found \" + tokens[0]);\n\t\t}\n\t\tString serverSecret = computeServerSecretApplicableAt(creationTime);\n\t\tString pseudoRandomNumber = tokens[1];\n\t\t// Permit extendedInfo to itself contain \":\" characters\n\t\tStringBuilder extendedInfo = new StringBuilder();\n\t\tfor (int i = 2; i < tokens.length - 1; i++) {\n\t\t\tif (i > 2) {\n\t\t\t\textendedInfo.append(\":\");\n\t\t\t}\n\t\t\textendedInfo.append(tokens[i]);\n\t\t}\n\t\tString sha1Hex = tokens[tokens.length - 1];\n\t\t// Verification\n\t\tString content = creationTime + \":\" + pseudoRandomNumber + \":\" + extendedInfo.toString();\n\t\tString expectedSha512Hex = Sha512DigestUtils.shaHex(content + \":\" + serverSecret);\n\t\tAssert.isTrue(Utf8.isEqual(expectedSha512Hex, sha1Hex), \"Key verification failure\");\n\t\treturn new DefaultToken(key, creationTime, extendedInfo.toString());\n\t}","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/core/src/main/java/org/springframework/security/core/token/KeyBasedPersistenceTokenService.java#L101-L137","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Regenerate the token by calling allocateToken again and have the user re-authenticate","Verify the token was not truncated or re-encoded in transit/storage (check URL-encoding, cookie handling)","Wrap verifyToken in try-catch (IllegalArgumentException) and treat failures as 'token invalid' rather than crashing"],"exampleFix":"// before\nToken token = service.verifyToken(rawToken);\n// after\ntry {\n    Token token = service.verifyToken(rawToken);\n} catch (IllegalArgumentException ex) {\n    // invalid token: force re-authentication\n    service.allocateToken(user.getKey());\n}","handlingStrategy":"try-catch","validationCode":"String[] tokens = rawToken.split(\":\"); boolean looksNumeric = tokens.length >= 4 && tokens[0].matches(\"(0[xX])?[0-9a-fA-F]+|[+-]?[0-9]+\");","typeGuard":"boolean plausiblyValidToken(String t) { return t != null && t.split(\":\").length >= 4; }","tryCatchPattern":"try { token = service.verifyToken(rawToken); } catch (IllegalArgumentException ex) { token = null; // treat as invalid: force re-auth }","preventionTips":["Always catch IllegalArgumentException around verifyToken","Never hand-edit or truncate token strings","Watch for cookie/URL encoding corrupting the ':' separators","Regenerate tokens after key rotation"],"tags":["token-validation","malformed-token","illegal-argument"],"backgroundTag":"invalid-argument-format","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}