jwtk/jjwt · error · UnsupportedJwtException

Unexpected Claims JWS.

Error message

Unexpected Claims JWS.

What it means

SupportedJwtVisitor.onVerifiedClaims is the default callback for a JWS that has been cryptographically verified and whose payload is a Claims JSON object. The base class throws UnsupportedJwtException by default, expecting a subclass to override this method. Hitting it means your parser dispatched a verified Claims JWS to a visitor that does not handle that token type.

Source

Thrown at api/src/main/java/io/jsonwebtoken/SupportedJwtVisitor.java:144

     * @throws UnsupportedJwtException by default, expecting the subclass implementation to override as necessary.
     */
    public T onVerifiedContent(Jws<byte[]> jws) {
        throw new UnsupportedJwtException("Unexpected content JWS.");
    }

    /**
     * Handles an encountered JWS message that has been cryptographically verified/authenticated and has a
     * {@link Claims} payload.
     *
     * <p>The default implementation immediately throws an {@link UnsupportedJwtException}; it is expected that
     * subclasses will override this method if the application needs to support this type of JWT.</p>
     *
     * @param jws the parsed signed (and verified) Claims JWS
     * @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.
     * @throws UnsupportedJwtException by default, expecting the subclass implementation to override as necessary.
     */
    public T onVerifiedClaims(Jws<Claims> jws) {
        throw new UnsupportedJwtException("Unexpected Claims JWS.");
    }

    /**
     * Handles an encountered JSON Web Encryption (aka 'JWE') message that has been authenticated and decrypted by
     * delegating to either {@link #onDecryptedContent(Jwe)} or {@link #onDecryptedClaims(Jwe)} depending on the
     * payload type.
     *
     * @param jwe the parsed authenticated and decrypted JWE.
     * @return the value returned by either {@link #onDecryptedContent(Jwe)} or {@link #onDecryptedClaims(Jwe)}
     * depending on the payload type.
     * @throws UnsupportedJwtException if the payload is neither a {@code byte[]} nor {@code Claims}, or either
     *                                 delegate method throws the same.
     */
    @SuppressWarnings("unchecked")
    @Override
    public T visit(Jwe<?> jwe) {
        Assert.notNull(jwe, "JWE cannot be null.");
        Object payload = jwe.getPayload();

View on GitHub (pinned to fb71496164)

Solutions

  1. Override onVerifiedClaims in your SupportedJwtVisitor subclass to inspect and return data from Jws<Claims>.
  2. Confirm the parse call matches the token type (use parseClaimsJws or equivalent instead of a generic visitor parse if Claims are the target).
  3. If Claims JWSs are not supported in this code path, reject the token upstream before parsing.
  4. Add a test that parses a signed Claims token through the visitor to catch missing overrides early.

Example fix

// before
public class ContentOnlyVisitor extends SupportedJwtVisitor<MyType> {
    @Override public MyType onVerifiedContent(Jws<byte[]> jws) { return handle(jws); }
    // onVerifiedClaims not overridden -> throws
}
// after
public class ContentOnlyVisitor extends SupportedJwtVisitor<MyType> {
    @Override public MyType onVerifiedContent(Jws<byte[]> jws) { return handle(jws); }
    @Override public MyType onVerifiedClaims(Jws<Claims> jws) { return handleClaims(jws.getPayload()); }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the visitor subclass overrides onVerifiedClaims before wiring it into the parser
Class<? extends SupportedJwtVisitor<?>> c = visitor.getClass();
boolean overridden = !SupportedJwtVisitor.class.equals(
    c.getMethod("onVerifiedClaims", Jws.class).getDeclaringClass());

Type guard

boolean visitorHandlesClaimsJws(SupportedJwtVisitor<?> v) {
    try {
        return !SupportedJwtVisitor.class.equals(
            v.getClass().getMethod("onVerifiedClaims", Jws.class).getDeclaringClass());
    } catch (NoSuchMethodException e) { return false; }
}

Try / catch

try {
    Jws<Claims> jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
} catch (UnsupportedJwtException e) {
    log.warn("Verified Claims JWS dispatched to a visitor without onVerifiedClaims", e);
    throw new SecurityException("Claims JWS not supported by this visitor", e);
}

Prevention

When it happens

Trigger: Parsing a properly signed token containing a Claims payload (e.g. created with Jwts.builder().claims()...signWith(key)) through a parser/visitor that does not override onVerifiedClaims — for example a visitor written only for content JWSs or JWEs.

Common situations: Custom visitor subclasses that only implemented some callbacks; refactors where parse() (visitor-dispatch) replaced parseClaimsJws() but the visitor was never extended; shared generic parsing pipelines receiving Claims JWSs they never anticipated.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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