jwtk/jjwt · error · MalformedJwtException
Compact JWT strings MUST always have a Base64Url protected h
Error message
Compact JWT strings MUST always have a Base64Url protected header per https://tools.ietf.org/html/rfc7519#section-7.2 (steps 2-4).
What it means
Every compact JWT must contain a Base64Url-encoded protected header as its first dot-separated segment per RFC 7519 §7.2. When tokenize() splits the compact string and the protected header segment is empty/blank, parse() throws MalformedJwtException.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:378
}
@Override
public Jwt<?, ?> parse(Reader reader) {
Assert.notNull(reader, "Reader cannot be null.");
return parse(reader, Payload.EMPTY);
}
private Jwt<?, ?> parse(Reader compact, Payload unencodedPayload) {
Assert.notNull(compact, "Compact reader cannot be null.");
Assert.stateNotNull(unencodedPayload, "internal error: unencodedPayload is null.");
final TokenizedJwt tokenized = jwtTokenizer.tokenize(compact);
final CharSequence base64UrlHeader = tokenized.getProtected();
if (!Strings.hasText(base64UrlHeader)) {
String msg = "Compact JWT strings MUST always have a Base64Url protected header per " +
"https://tools.ietf.org/html/rfc7519#section-7.2 (steps 2-4).";
throw new MalformedJwtException(msg);
}
// =============== Header =================
final byte[] headerBytes = decode(base64UrlHeader, "protected header");
Map<String, ?> m = deserialize(Streams.of(headerBytes), "protected header");
Header header;
try {
header = tokenized.createHeader(m);
} catch (Exception e) {
String msg = "Invalid protected header: " + e.getMessage();
throw new MalformedJwtException(msg, e);
}
// https://tools.ietf.org/html/rfc7515#section-10.7 , second-to-last bullet point, note the use of 'always':
//
// * Require that the "alg" Header Parameter be carried in the JWS
// Protected Header. (This is always the case when using the JWS
// Compact Serialization and is the approach taken by CMS [RFC6211].)View on GitHub (pinned to fb71496164)
Solutions
- Check the token is a complete three- (or five-) segment compact JWT before parsing
- Strip any 'Bearer ' prefix and trim whitespace from the credential value
- Fix upstream code (storage/logging) that truncates or loses the token
Example fix
// before
String token = authHeader.split(" ")[1]; // could yield partial token
// after
String token = authHeader.replaceFirst("^Bearer\\s+", "").trim();
if (token.chars().filter(c -> c == '.').count() != 2) throw new MalformedJwtException("bad token"); Defensive patterns
Strategy: validation
Validate before calling
if (token == null || token.chars().filter(c -> c == '.').count() != 2) {
throw new MalformedJwtException("Compact JWT must have 3 dot-separated segments");
} Try / catch
try {
return parser.parseSignedClaims(token);
} catch (MalformedJwtException e) {
throw new UnauthorizedException("Malformed JWT", e);
} Prevention
- Strip 'Bearer ' prefixes and trim before parsing
- Validate segment count before calling parse
- Never log/store truncated tokens as credentials
When it happens
Trigger: Calling parse/parseSignedClaims/parseSignedContent with a string missing the header segment: e.g. ".payload.signature", "..", a bare signature, an empty string, or a truncated token cut at the first dot.
Common situations: Tokens truncated during logging/storage; string splitting or slicing bugs; passing authorization header values like 'Bearer ' prefix remnants or only the signature; reading tokens from config fields that are empty.
Related errors
- Invalid Base64Url <name>: <value>
- JWS header does not contain a required 'alg' (Algorithm) hea
- JWEs do not support key management alg header value 'none' p
- The JWS header references signature algorithm 'none' yet the
- Unsecured JWSs (those with an alg header value of 'none') ma
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/e220cf9b05311ee9.
Report an issue: GitHub.