jwtk/jjwt · error · DeserializationException
${String.format(MALFORMED_COMPLEX_ERROR, this.name, this.nam
Error message
${String.format(MALFORMED_COMPLEX_ERROR, this.name, this.name, e.getMessage())} What it means
Thrown as a DeserializationException when deserializing a JSON structure causes a StackOverflowError. jjwt catches this specifically because deeply nested JSON payloads (e.g. thousands of '[' or '{' characters) overflow the recursive parser and would otherwise crash the JVM thread. The formatted message names the value being parsed ('MALFORMED_COMPLEX_ERROR') and the underlying cause.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/io/JsonObjectDeserializer.java:70
value = this.deserializer.deserialize(in);
if (value == null) {
String msg = "Deserialized data resulted in a null value; cannot create Map<String,?>";
throw new DeserializationException(msg);
}
if (!(value instanceof Map)) {
String msg = "Deserialized data is not a JSON Object; cannot create Map<String,?>";
throw new DeserializationException(msg);
}
// JSON Specification requires all JSON Objects to have string-only keys. So instead of
// checking that the val.keySet() has all Strings, we blindly cast to a Map<String,?>
// since input would rarely, if ever, have non-string keys.
//noinspection unchecked
return (Map<String, ?>) value;
} catch (StackOverflowError e) {
String msg = String.format(MALFORMED_COMPLEX_ERROR, this.name, this.name, e.getMessage());
throw new DeserializationException(msg, e);
} catch (Throwable t) {
throw malformed(t);
}
}
protected RuntimeException malformed(Throwable t) {
String msg = String.format(MALFORMED_ERROR, this.name, t.getMessage());
throw new MalformedJwtException(msg, t);
}
}
View on GitHub (pinned to fb71496164)
Solutions
- Treat the token as malformed and reject it — do not retry; the input is the problem.
- Validate token structure (e.g. limit payload size and nesting) before passing it to the parser.
- Increase thread stack size (-Xss) only as a stopgap; prefer rejecting the input.
- Catch DeserializationException (or MalformedJwtException's superclass JwtException) and return 4xx to the caller.
Example fix
// before
Claims c = Jwts.parser().verifyWith(key).build().parseSignedClaims(token).getPayload(); // DeserializationException on deep nesting
// after
try {
Claims c = Jwts.parser().verifyWith(key).build().parseSignedClaims(token).getPayload();
} catch (MalformedJwtException e) {
throw new BadJwtTokenException("Malformed JWT payload", e);
} Defensive patterns
Strategy: try-catch
Validate before calling
// reject tokens with suspiciously large or deep payloads before parsing
if (token.length() > MAX_TOKEN_LENGTH) throw new BadJwtTokenException("JWT too large"); Type guard
// verify the compact JWT has exactly 3-5 dot-separated base64url segments
boolean wellFormed = token != null && token.matches("^[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+){2,4}$"); Try / catch
try {
return Jwts.parser().verifyWith(key).build().parseSignedClaims(token);
} catch (MalformedJwtException | DeserializationException e) {
throw new BadJwtTokenException("Malformed JWT", e);
} Prevention
- Treat untrusted JWTs as hostile; cap payload size
- Catch JwtException subtypes at the authentication boundary
- Never retry parsing a token that failed deserialization
- Keep jjwt updated for parser hardening fixes
When it happens
Trigger: Parsing a JWT whose payload or claims map contains extremely deeply nested JSON arrays/objects, so the streaming JSON deserializer recurses past the stack limit. Triggered via Jwts.parser().parse... on a maliciously or accidentally crafted token.
Common situations: Receiving untrusted JWTs from third parties; security fuzzing; logging giant nested payloads; test suites checking malformed-token handling.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Unable to deserialize: ${t.getMessage()}
- Malformed or excessively complex ${name} JSON. If experience
- Malformed ${name} JSON: ${t.getMessage()}
- Unable to serialize object of type ${className}: ${e.getMess
- Cannot convert existing claim value of type '%s' to desired
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/428816e4c927d8c1.
Report an issue: GitHub.