jwtk/jjwt · error · MalformedJwtException
JWE header does not contain a required 'alg' (Algorithm)…
Error message
JWE header does not contain a required 'alg' (Algorithm) header parameter. This header parameter is mandatory per the JWE Specification, Section 4.1.1. See https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.1 for more information.
What it means
The JWE specification (RFC 7516 §4.1.1) mandates an 'alg' (Algorithm) header parameter identifying the key-management algorithm. When parsing a tokenized JWE whose header lacks a usable 'alg' value, parse() throws MalformedJwtException with this message (distinguished from the JWS variant via MISSING_JWE_ALG_MSG).
Solutions
- Fix the token producer to include 'alg' in the protected header (e.g. Jwts.builder().header().algorithm(...)).encryptWith(...)
- Decode the first segment of the failing token to confirm 'alg' is missing and reject such tokens at the boundary
- If another library produced the token, configure it to put alg/enc in the protected header per RFC 7516
Example fix
// before
String jwe = Jwts.builder().setClaims(claims)
.encryptWith(key, encAlg).compact(); // no alg header
// after
String jwe = Jwts.builder().setClaims(claims)
.header().add("alg", keyAlg.getId()).and()
.encryptWith(key, keyAlg, encAlg).compact(); Defensive patterns
Strategy: validation
Validate before calling
String headerJson = new String(Base64.getUrlDecoder().decode(token.split("\\.")[0]), StandardCharsets.UTF_8);
if (!headerJson.contains("\"alg\"")) {
throw new MalformedJwtException("JWE header missing required 'alg'");
} Try / catch
try {
return parser.parseEncryptedClaims(token, decryptKey);
} catch (MalformedJwtException e) {
if (e.getMessage().contains("'alg'")) {
throw new UnauthorizedException("Non-compliant JWE: missing alg", e);
}
throw e;
} Prevention
- Always build JWEs with the library's encryptWith(...) so mandatory headers are set
- Verify third-party producers emit RFC 7516 compliant protected headers
- Validate presence of alg/enc in header during token intake
When it happens
Trigger: Parsing an encrypted JWT (five-segment compact form) whose protected header omits 'alg' or has an empty/whitespace-only value — typically a token produced by a non-compliant encryptor or a hand-assembled header.
Common situations: Custom/buggy encryption code building headers manually; other libraries that place 'alg' in the unprotected (shared) header instead of the protected header; tokens mutated so header fields were dropped.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Both 'signWith' and 'encryptWith' cannot be specified…
- Compact JWE strings MUST always contain a payload…
- keys may not be used with ECDH-ES key agreement algorithms…
- Encrypted JWTs must have either 'claims' or non-empty…
- [JWA RFC 7518, Section…
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/22676b45474f49ab.
Report an issue: GitHub.
Appendix: source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:401
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].)
//
final String alg = Strings.clean(header.getAlgorithm());
if (!Strings.hasText(alg)) {
String msg = tokenized instanceof TokenizedJwe ? MISSING_JWE_ALG_MSG : MISSING_JWS_ALG_MSG;
throw new MalformedJwtException(msg);
}
final boolean unsecured = Jwts.SIG.NONE.getId().equalsIgnoreCase(alg);
final CharSequence base64UrlDigest = tokenized.getDigest();
final boolean hasDigest = Strings.hasText(base64UrlDigest);
if (unsecured) {
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())) {View on GitHub (pinned to fb71496164)