jwtk/jjwt · error · IllegalArgumentException
String value is not a JWT NumericDate, nor is it ISO-8601-fo
Error message
String value is not a JWT NumericDate, nor is it ISO-8601-formatted. All heuristics exhausted. Cause: ${e.getMessage()} What it means
Thrown by JwtDateConverter.parseIso8601Date when a string claim value (e.g. 'exp', 'iat', 'nbf') cannot be parsed as a JWT NumericDate nor as an ISO-8601 date, after all parsing heuristics failed. The underlying ParseException message is appended as the cause. It surfaces as an IllegalArgumentException while converting date claims during JWT construction or parsing.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/lang/JwtDateConverter.java:108
String msg = "Cannot create Date from object of type " + v.getClass().getName() + ".";
throw new IllegalArgumentException(msg);
}
}
/**
* Parses the specified ISO-8601-formatted string and returns the corresponding {@link Date} instance.
*
* @param value an ISO-8601-formatted string.
* @return a {@link Date} instance reflecting the specified ISO-8601-formatted string.
* @since 0.10.0
*/
private static Date parseIso8601Date(String value) throws IllegalArgumentException {
try {
return DateFormats.parseIso8601Date(value);
} catch (ParseException e) {
String msg = "String value is not a JWT NumericDate, nor is it ISO-8601-formatted. " +
"All heuristics exhausted. Cause: " + e.getMessage();
throw new IllegalArgumentException(msg, e);
}
}
}
View on GitHub (pinned to fb71496164)
Solutions
- Pass java.util.Date, java.time.Instant or a numeric seconds-since-epoch value instead of a string for date claims.
- If a string is required, format it as ISO-8601, e.g. '2026-09-08T12:00:00Z'.
- Catch IllegalArgumentException and log the cause message to identify the malformed value.
Example fix
// before
jwtBuilder.claim("exp", "tomorrow afternoon");
// after
jwtBuilder.expiration(new Date(System.currentTimeMillis() + 3600_000L)); Defensive patterns
Strategy: validation
Validate before calling
boolean validDateClaim(Object v) {
if (v instanceof Number || v instanceof java.util.Date || v instanceof java.time.Instant) return true;
if (v instanceof String s) {
try { Long.parseLong(s); return true; } catch (NumberFormatException ignored) {}
try { java.time.OffsetDateTime.parse(s); return true; } catch (Exception ignored) {}
}
return false;
} Try / catch
try {
jwtBuilder.claim("exp", rawValue);
} catch (IllegalArgumentException e) {
// message starts with "String value is not a JWT NumericDate..."
throw new IllegalArgumentException("Invalid date claim value: " + rawValue, e);
} Prevention
- Always pass Date/Instant objects for date claims instead of strings
- Use JwtBuilder's typed expiration()/issuedAt()/notBefore() methods
- Never copy raw string date claims between tokens without revalidating
When it happens
Trigger: Setting a date claim to a non-numeric, non-ISO-8601 string such as header.put("exp", "tomorrow") or passing arbitrary text into a converter expecting a date representation.
Common situations: Hand-built JWTs where date claims are supplied as raw strings instead of Date/Instant objects; copying claim values from another token with wrong formatting; locale-specific date strings like '12/31/2026'.
Related errors
- Unexpected unsecured Claims JWT.
- Unexpected content JWS.
- Unexpected Claims JWS.
- Unexpected content JWE.
- Unexpected Claims JWE.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/9d7c72f456f79ddf.
Report an issue: GitHub.