jwtk/jjwt · error · DeserializationException
Unable to deserialize: ${t.getMessage()}
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.
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)
Solutions
- Inspect the exception's getCause() to find the underlying parser error and fix that root cause first
- Verify the token/payload string is valid, complete JSON (not truncated, not HTML, no whitespace corruption from URL handling)
- Ensure a supported JSON runtime (Jackson or Gson) is on the classpath at a version compatible with your JJWT release
- 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
- Always catch DeserializationException around parse calls and inspect getCause()
- Validate tokens are complete, unmodified JWTs (three dot-separated base64url segments) before parsing
- Trim and denormalize token strings (remove whitespace/newlines from copy-paste or storage wrapping)
- Keep your JSON runtime (Jackson/Gson) at a version compatible with your JJWT release
- Return DeserializationException from any custom Deserializer's doDeserialize
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
- Malformed ${name} JSON: ${t.getMessage()}
- ${String.format(MALFORMED_COMPLEX_ERROR, this.name, this.nam
- Unable to serialize object of type ${className}: ${e.getMess
- Cannot convert existing claim value of type '%s' to desired
- Invalid protected header: ${e.getMessage()}
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/b4c4ef7ee08737d5.
Report an issue: GitHub.