jwtk/jjwt · error · DeserializationException

Unable to deserialize

Error message

Unable to deserialize: ${t.getMessage()}

What it means

io.jsonwebtoken (JJWT) wraps any unexpected Throwable thrown while deserializing a byte/char stream back into a Java object into a DeserializationException. This wrapper is produced by AbstractDeserializer.deserialize when doDeserialize throws something that is not already a DeserializationException (e.g. a RuntimeException from the underlying parser). The original cause is preserved via the cause chain, so inspect getCause() for the real problem.

Solutions

  1. Inspect the exception's getCause() to find the underlying parser error and fix that root cause first
  2. Verify the token/payload string is valid, complete JSON (not truncated, not HTML, no whitespace corruption from URL handling)
  3. Ensure a supported JSON runtime (Jackson or Gson) is on the classpath at a version compatible with your JJWT release
  4. If you implemented a custom Deserializer, wrap parser failures in DeserializationException inside doDeserialize so the original error type is propagated

Example fix

// before
Claims claims = Jwts.parserBuilder().build()
    .parseClaimsJws(corruptedToken).getBody(); // Unable to deserialize: ...

// after
try {
    Claims claims = Jwts.parserBuilder().build()
        .parseClaimsJws(token.trim()).getBody();
} catch (DeserializationException e) {
    log.error("Bad token payload: {}", e.getCause()); // inspect root cause
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Java: sanity-check the token text before parsing
boolean isPlausibleJwt(String token) {
    return token != null && token.chars().filter(c -> c == '.').count() == 2
        && !token.isBlank();
}

Type guard

boolean isDeserializationSafe(java.io.Reader r) {
    return r != null; // content validity is delegated to the parser; guard nulls and catch DeserializationException
}

Try / catch

try {
    Claims claims = Jwts.parserBuilder().build().parseClaimsJws(token).getBody();
} catch (DeserializationException e) {
    Throwable root = e.getCause();
    // handle malformed payload; log root cause
}

Prevention

When it happens

Trigger: Calling any JJWT API that must deserialize a payload or claim map (e.g. Jwts.parser().parseClaimsJws(...) with a serialized body, or a custom Serializer/Deserializer implementation whose doDeserialize throws an undeclared exception such as Jackson's JsonParseException, ClassNotFoundException, or a NullPointerException).

Common situations: Passing truncated, corrupted, or non-JSON base64url payload text to the parser; a classpath mismatch where the Jackson/Gson runtime is a different version than JJWT expects; deserializing a payload into a custom type whose class is missing at runtime; a custom Deserializer that throws raw exceptions instead of DeserializationException.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at api/src/main/java/io/jsonwebtoken/io/AbstractDeserializer.java:72

        InputStream in = new ByteArrayInputStream(bytes);
        Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
        return deserialize(reader);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final T deserialize(Reader reader) throws DeserializationException {
        Assert.notNull(reader, "Reader argument cannot be null.");
        try {
            return doDeserialize(reader);
        } catch (Throwable t) {
            if (t instanceof DeserializationException) {
                throw (DeserializationException) t;
            }
            String msg = "Unable to deserialize: " + t.getMessage();
            throw new DeserializationException(msg, t);
        }
    }

    /**
     * Reads the specified character stream and returns the corresponding Java object.
     *
     * @param reader the reader to use to read the character stream
     * @return the deserialized Java object
     * @throws Exception if there is a problem reading the stream or creating the expected Java object
     */
    protected abstract T doDeserialize(Reader reader) throws Exception;
}

View on GitHub (pinned to fb71496164)