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

  1. Reject or rate-limit inputs whose nesting depth exceeds a sane limit before parsing.
  2. Validate the token source — this message specifically suggests a potentially malicious payload.
  3. Add a pre-parse check on input size and brace-nesting depth (e.g. regex or scanner counting '{').
  4. 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

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.

Related errors


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