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

  1. Pass java.util.Date, java.time.Instant or a numeric seconds-since-epoch value instead of a string for date claims.
  2. If a string is required, format it as ISO-8601, e.g. '2026-09-08T12:00:00Z'.
  3. 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

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


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/9d7c72f456f79ddf. Report an issue: GitHub.