jwtk/jjwt · error · IncorrectClaimException
JWT Claim '<expectedClaimName>' was expected to be a Date, b
Error message
JWT Claim '<expectedClaimName>' was expected to be a Date, but its value cannot be converted to a Date using current heuristics. Value: <actualClaimValue>
What it means
Thrown as IncorrectClaimException by require(...) claim validation when the expected claim value passed to the parser is a Date (or Calendar), but the actual claim in the token cannot be converted to a Date using JJWT's heuristics (long epoch millis, ISO-8601 string, etc.). This means a requiredDate(...) call found a claim whose value is of an unparseable type/format.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:744
return o;
}
private void validateExpectedClaims(Header header, Claims claims) {
final Claims expected = expectedClaims.build();
for (String expectedClaimName : expected.keySet()) {
Object expectedClaimValue = normalize(expected.get(expectedClaimName));
Object actualClaimValue = normalize(claims.get(expectedClaimName));
if (expectedClaimValue instanceof Date) {
try {
actualClaimValue = claims.get(expectedClaimName, Date.class);
} catch (Exception e) {
String msg = "JWT Claim '" + expectedClaimName + "' was expected to be a Date, but its value " +
"cannot be converted to a Date using current heuristics. Value: " + actualClaimValue;
throw new IncorrectClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);
}
}
if (actualClaimValue == null) {
boolean collection = expectedClaimValue instanceof Collection;
String msg = "Missing '" + expectedClaimName + "' claim. Expected value";
if (collection) {
msg += "s: " + expectedClaimValue;
} else {
msg += ": " + expectedClaimValue;
}
throw new MissingClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);
} else if (expectedClaimValue instanceof Collection) {
Collection<?> expectedValues = (Collection<?>) expectedClaimValue;
Collection<?> actualValues = actualClaimValue instanceof Collection ? (Collection<?>) actualClaimValue :
Collections.setOf(actualClaimValue);
for (Object expectedValue : expectedValues) {
if (!Collections.contains(actualValues.iterator(), expectedValue)) {View on GitHub (pinned to fb71496164)
Solutions
- Fix the token producer to emit the claim as epoch-millis (long) or ISO-8601 date string.
- Inspect the actual token payload (base64-decode the claims section) to see the raw value and adjust your expectation type.
- If the claim is genuinely not a date, change the require() call to require(name, correctExpectedValue) of matching type.
- Add a custom claim converter/serializer on the issuing side so dates round-trip in a standard format.
Example fix
// before
String jwt = Jwts.builder().claim("iat", "yesterday").compact(); // unparseable
Claims c = Jwts.parser().build().parseSignedClaims(jwt).getPayload();
Jwts.parser().require("iat", new Date()).build().parse(jwt); // IncorrectClaimException
// after
String jwt = Jwts.builder().claim("iat", System.currentTimeMillis()).compact();
Jwts.parser().require("iat", new Date()).build().parse(jwt); // OK Defensive patterns
Strategy: validation
Validate before calling
Object raw = io.jsonwebtoken.Jwts.parser().build().parseUnsecuredClaims(jwt).getPayload().get("iat");
boolean isDateLike = raw instanceof Number ||
(raw instanceof String s && s.matches("\\d{10,13}|\\d{4}-\\d{2}-\\d{2}T.*"));
if (!isDateLike) throw new IllegalArgumentException("Claim 'iat' is not date-like: " + raw); Type guard
boolean isDateLike(Object v) {
return v instanceof Date || v instanceof Number
|| (v instanceof String s && (s.matches("\\d+") || s.matches("\\d{4}-\\d{2}-\\d{2}T.+")));
} Try / catch
try {
Jwts.parser().require("exp", expiryDate).build().parse(jwt);
} catch (io.jsonwebtoken.IncorrectClaimException e) {
// inspect e.getClaimName()/value; producer emitted a non-date format
} Prevention
- Emit date claims as epoch-millis longs or ISO-8601 strings only
- Contract-test the producer's claim types against consumer expectations
- Never put human-readable dates or nested objects in date claims
- Round-trip test: parse tokens your builder creates before shipping
When it happens
Trigger: parser.require(claimName, someDate) where the JWT contains claimName with a value that is neither a numeric epoch-millis nor a recognized date string (e.g. an arbitrary object, boolean, or malformed text).
Common situations: Issuer serializes dates as non-standard formats (e.g. '01/02/2024', human-readable text, or nested objects); a JWT library on the producing side emits dates as seconds-instead-of-millis strings with non-numeric characters; claims populated programmatically with wrong types.
Related errors
- ${message}Object of class [${objClassName}] must be an insta
- ${message}${subType} is not assignable to ${superType}
- Missing '<expectedClaimName>' claim. Expected value: <expect
- Missing expected '<expectedValue>' value in '<expectedClaimN
- Expected <expectedClaimName> claim to be: <expectedClaimValu
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/9480e560b4c48419.
Report an issue: GitHub.