jwtk/jjwt · warning · io.jsonwebtoken.io.DeserializationException
Malformed or excessively complex ${name} JSON. If experience
Error message
Malformed or excessively complex ${name} JSON. If experienced in a production environment, this could reflect a potential malicious ${name}, please investigate the source further. Cause: ${e.getMessage()} What it means
Thrown as a DeserializationException with a security-oriented message when deserialization causes a StackOverflowError — typically deeply nested JSON that exceeds stack depth. Because deep nesting can indicate a malicious payload, JJWT surfaces an explicit warning recommending investigation of the source.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/io/JsonObjectDeserializer.java:68
Object value;
try {
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
- Reject or rate-limit inputs whose nesting depth exceeds a sane limit before parsing.
- Validate the token source — this message specifically suggests a potentially malicious payload.
- Add a pre-parse check on input size and brace-nesting depth (e.g. regex or scanner counting '{').
- Consider limiting maximum JWT length at the gateway/ingress layer.
Example fix
// before
Jwts.parser().build().parseClaimsJws(token); // parses any depth
// after
if (token != null && token.length() > MAX_TOKEN_LENGTH) {
throw new IllegalArgumentException("JWT exceeds maximum allowed length");
}
Jwts.parser().build().parseClaimsJws(token); Defensive patterns
Strategy: validation
Validate before calling
// Java
private static final int MAX_DEPTH = 64;
static int nestingDepth(String json) {
int depth = 0, max = 0;
for (char c : json.toCharArray()) {
if (c == '{' || c == '[') max = Math.max(max, ++depth);
else if (c == '}' || c == ']') depth--;
}
return max;
}
if (nestingDepth(token) > MAX_DEPTH) throw new SecurityException("JWT nesting too deep"); Type guard
static boolean isPlausiblySafeJson(String json) {
return json != null && json.length() <= 65536 && nestingDepth(json) <= 64;
} Try / catch
try {
Jws<Claims> jws = Jwts.parser().build().parseClaimsJws(token);
} catch (DeserializationException e) {
if (e.getMessage().contains("Malformed or excessively complex")) {
securityLog.warn("Potential malicious payload from {}", requestSource);
}
throw new SecurityException("Rejected suspicious token");
} Prevention
- Enforce maximum JWT length and nesting depth at your API gateway.
- Rate-limit and log sources submitting deeply nested tokens.
- Keep the JJWT library updated — parser hardening improves over versions.
- Treat this error as a security signal, not just a parse failure, and review the token source.
When it happens
Trigger: Deserializing JSON with extremely deep object/array nesting (e.g. thousands of nested '{' characters) that overflows the stack in the underlying parser.
Common situations: Malicious or fuzzed tokens submitted to a public endpoint; upstream systems generating deeply nested claim structures; unbounded recursion in attacker-controlled JWT payloads (a known DoS vector).
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ${String.format(MALFORMED_COMPLEX_ERROR, this.name, this.nam
- Unable to deserialize: ${t.getMessage()}
- Deserialized data resulted in a null value; cannot create Ma
- Deserialized data is not a JSON Object; cannot create Map<St
- Malformed ${name} JSON: ${t.getMessage()}
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/c6275d4f688d83d6.
Report an issue: GitHub.