jwtk/jjwt · error · PrematureJwtException

JWT early by <differenceMillis> milliseconds before <nbfVal>

Error message

JWT early by <differenceMillis> milliseconds before <nbfVal>. Current time: <nowVal>. Allowed clock skew: <allowedClockSkewMillis> milliseconds.

What it means

Thrown as PrematureJwtException during JWT parsing when the token's 'nbf' (not-before) claim is later than the current time, even after allowing the configured clock skew (default 0 unless setClockSkewSeconds was called). JJWT treats an nbf in the future as 'this token is not valid yet' and refuses to parse it. The message quantifies how many milliseconds early the token is.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:709

            }

            // https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5
            // token MUST NOT be accepted before any specified nbf time:
            Date nbf = claims.getNotBefore();
            if (nbf != null) {

                long minTime = nowTime + this.allowedClockSkewMillis;
                Date min = allowSkew ? new Date(minTime) : now;
                if (min.before(nbf)) {
                    String nbfVal = DateFormats.formatIso8601(nbf, true);
                    String nowVal = DateFormats.formatIso8601(now, true);

                    long differenceMillis = nbf.getTime() - nowTime;

                    String msg = "JWT early by " + differenceMillis + " milliseconds before " + nbfVal +
                            ". Current time: " + nowVal + ". Allowed clock skew: " +
                            this.allowedClockSkewMillis + " milliseconds.";
                    throw new PrematureJwtException(header, claims, msg);
                }
            }

            validateExpectedClaims(header, claims);
        }

        return jwt;
    }

    /**
     * @since 0.10.0
     */
    private static Object normalize(Object o) {
        if (o instanceof Integer) {
            o = ((Integer) o).longValue();
        }
        return o;
    }

View on GitHub (pinned to fb71496164)

Solutions

  1. Synchronize clocks on the issuing and verifying hosts (NTP/chrony).
  2. Increase allowed clock skew: parserBuilder.setClockSkewSeconds(...) to cover the drift window.
  3. Regenerate the token with an nbf <= now (or omit nbf) if the issuer set it incorrectly.
  4. If tests intentionally use future nbf, set the parser's Clock to a fixed date at or after nbf via parserBuilder.setClock(Clock.fixed(...)).

Example fix

// before
Jwts.parser().verifyWith(key).build().parseSignedClaims(jwt); // PrematureJwtException
// after
Claims claims = Jwts.parser()
    .clockSkewSeconds(300) // tolerate up to 5 min of clock drift
    .verifyWith(key)
    .build()
    .parseSignedClaims(jwt).getPayload();
Defensive patterns

Strategy: try-catch

Validate before calling

// decode payload without verification and inspect nbf
String[] parts = jwt.split("\\.");
String payload = new String(java.util.Base64.getUrlDecoder().decode(parts[1]));
long nbf = com.fasterxml.jackson.databind.json.JsonMapper.builder().build()
    .readTree(payload).path("nbf").asLong(0);
if (nbf > System.currentTimeMillis() + 300_000) {
    throw new IllegalStateException("Token not valid yet (nbf too far in future)");
}

Try / catch

try {
    claims = Jwts.parser().clockSkewSeconds(300).verifyWith(key).build().parseSignedClaims(jwt).getPayload();
} catch (io.jsonwebtoken.PrematureJwtException e) {
    // token not yet valid; retry after e's indicated delay or reject
}

Prevention

When it happens

Trigger: Calling jwtParser.parse(...) / parseSignedClaims(...) / parseSignedContent(...) on a JWT whose 'nbf' claim timestamp is after the system clock of the machine doing the parsing, and the difference exceeds allowedClockSkewMillis.

Common situations: Clock drift between the token issuer's server and the verifier; issuing tokens with an nbf set slightly in the future by mistake; tokens minted on a machine with a fast clock and validated on one with a slow clock; container/KVM environments where clocks aren't synchronized (no NTP).

Related errors


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