jwtk/jjwt · error · MalformedJwtException
The JWE header references key management algorithm '%s' but
Error message
The JWE header references key management algorithm '%s' but the compact JWE string is missing the required AAD authentication tag.
What it means
For JWEs with a key-management algorithm other than 'none', the compact string must include the final AAD authentication tag segment; jjwt throws MalformedJwtException when the digest (AEAD tag) component is missing. This check lives in the shared `!hasDigest` branch, producing the JWE-specific message format for TokenizedJwe tokens.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:426
if (tokenized instanceof TokenizedJwe) {
throw new MalformedJwtException(JWE_NONE_MSG);
}
// Unsecured JWTs are disabled by default per the RFC:
if (!this.unsecured) {
String msg = UNSECURED_DISABLED_MSG_PREFIX + header;
throw new UnsupportedJwtException(msg);
}
if (hasDigest) {
throw new MalformedJwtException(JWS_NONE_SIG_MISMATCH_MSG);
}
if (header.containsKey(DefaultProtectedHeader.CRIT.getId())) {
String msg = String.format(CRIT_UNSECURED_MSG, header);
throw new MalformedJwtException(msg);
}
} else if (!hasDigest) { // something other than 'none'. Must have a digest component:
String fmt = tokenized instanceof TokenizedJwe ? MISSING_JWE_DIGEST_MSG_FMT : MISSING_JWS_DIGEST_MSG_FMT;
String msg = String.format(fmt, alg);
throw new MalformedJwtException(msg);
}
// ----- crit assertions -----
if (header instanceof ProtectedHeader) {
Set<String> crit = Collections.nullSafe(((ProtectedHeader) header).getCritical());
Set<String> supportedCrit = this.critical;
String b64Id = DefaultJwsHeader.B64.getId();
if (!unencodedPayload.isEmpty() && !this.critical.contains(b64Id)) {
// The application developer explicitly indicates they're using a B64 payload, so
// ensure that the B64 crit header is supported, even if they forgot to configure it on the
// parser builder:
supportedCrit = new LinkedHashSet<>(Collections.size(this.critical) + 1);
supportedCrit.add(DefaultJwsHeader.B64.getId());
supportedCrit.addAll(this.critical);
}
// assert any values per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11:
for (String name : crit) {
if (!header.containsKey(name)) {
String msg = String.format(CRIT_MISSING_MSG, name, name, header);View on GitHub (pinned to fb71496164)
Solutions
- Re-obtain the complete JWE compact string including all five segments (protected header, encrypted key, IV, ciphertext, auth tag).
- Verify the token has exactly 4 dot separators before parsing as JWE.
- Fix the producer/serializer to always emit the authentication tag (use Jwts.builder().encryptWith(...)).
- Check for transport-layer truncation (loggers, headers, query strings cutting the token).
Example fix
// before: truncated JWE (missing auth tag) String jwe = header + "." + key + "." + iv + "." + ciphertext; // after: full 5-part JWE String jwe = header + "." + key + "." + iv + "." + ciphertext + "." + tag; // or generate: Jwts.builder().claims(c).encryptWith(k, alg, enc).compact();
Defensive patterns
Strategy: validation
Validate before calling
String[] parts = jwe.split("\\.", -1);
if (parts.length != 5 || parts[4].isEmpty()) throw new IllegalArgumentException("JWE must have 5 non-empty parts including the auth tag"); Try / catch
try { return parser.parse(jwe); }
catch (io.jsonwebtoken.MalformedJwtException e) { throw new InvalidTokenException("JWE missing authentication tag", e); } Prevention
- Validate token shape before parsing
- Watch for loggers/URLs truncating tokens
- Generate JWEs only via Jwts.builder().encryptWith(...)
- Re-fetch the token rather than parsing corrupted copies
When it happens
Trigger: Calling parse()/parseSignedClaims() on a JWE compact string whose header declares a real key-management alg but which lacks the fifth (auth tag) segment — e.g. a truncated or wrongly serialized 4-segment string.
Common situations: Truncation during copy/paste, logging, or transport; custom serializers emitting only four JWE parts; confusion with JWS 3-segment format; misrouting a JWS string into a JWE parse path with mismatched header edits.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- JWEs do not support key management alg header value 'none' p
- Compact JWE strings MUST always contain a payload (ciphertex
- Unexpected content JWE.
- Unexpected Claims JWE.
- PrivateKeys may not be used to encrypt data. PublicKeys are
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/161da12878fccbeb.
Report an issue: GitHub.