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
- Inspect t.getMessage() in the cause to see the exact parser error and character position.
- Verify the token was not truncated or URL-encoded/decoded incorrectly in transit.
- Ensure you are decoding the correct base64url segment (payload for claims, header for header).
- Use JJWT's parse APIs with the correct key; mismatched keys can lead to garbage after failed MAC verification paths.
- 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
- Validate the compact JWS structure (3 or 5 dot-separated base64url segments) before parsing.
- Avoid ad-hoc base64 conversions that corrupt token bytes; use Decoders.BASE64URL.
- Reject tokens that arrived URL-encoded or HTML-escaped.
- Never modify token strings after receiving them; log the exact raw value when debugging.
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.
- 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()}
- ${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/2dcc95f8be404071.
Report an issue: GitHub.