jwtk/jjwt · error · io.jsonwebtoken.MalformedJwtException

Malformed ${name} JSON: ${t.getMessage()}

Error message

Malformed ${name} JSON: ${t.getMessage()}

What it means

Thrown as a MalformedJwtException when deserializing the named JSON fails for any reason other than a null/non-object result or a StackOverflowError — typically syntactically invalid JSON. The message identifies which named part (e.g. 'header', 'payload') was malformed and includes the parser's cause message.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/io/JsonObjectDeserializer.java:76

                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. Inspect t.getMessage() in the cause to see the exact parser error and character position.
  2. Verify the token was not truncated or URL-encoded/decoded incorrectly in transit.
  3. Ensure you are decoding the correct base64url segment (payload for claims, header for header).
  4. Use JJWT's parse APIs with the correct key; mismatched keys can lead to garbage after failed MAC verification paths.
  5. Re-obtain the token from the source if corruption is suspected.

Example fix

// before
String part = token.split("\\.")[1].replace("-", "+").replace("_", "/"); // ad-hoc decode can corrupt
// after
byte[] json = io.jsonwebtoken.io.Decoders.BASE64URL.decode(part);
String jsonStr = new String(json, StandardCharsets.UTF_8); // verify valid JSON before parsing
Defensive patterns

Strategy: try-catch

Validate before calling

// Java
String[] parts = token.split("\\.");
if (parts.length < 2) throw new IllegalArgumentException("not a compact JWT");
byte[] payload = Decoders.BASE64URL.decode(parts[1]);
String json = new String(payload, StandardCharsets.UTF_8);
if (!json.trim().startsWith("{")) throw new IllegalArgumentException("payload is not JSON");

Type guard

static boolean looksLikeJwt(String token) {
    if (token == null) return false;
    String[] parts = token.split("\\.");
    return parts.length == 3 || parts.length == 5;
}

Try / catch

try {
    Jws<Claims> jws = Jwts.parser().verifyWith(key).build().parseClaimsJws(token);
} catch (MalformedJwtException e) {
    log.warn("Malformed JWT rejected: {}", e.getMessage());
    throw new BadRequestException("Invalid token format");
}

Prevention

When it happens

Trigger: Calling apply/deserialize on JsonObjectDeserializer with content the underlying JSON parser cannot parse: truncated JSON, invalid tokens, wrong encoding, or non-UTF-8 bytes.

Common situations: Hand-truncated JWTs; tokens altered in transit (URL encoding/decoding damage); tokens signed over a payload that was later modified; passing the full compact JWS string where only a segment was expected.

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.

Related errors


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