jwtk/jjwt · error · MalformedJwtException
Protected Header crit set references header name '%s', but t
Error message
Protected Header crit set references header name '%s', but the header does not contain an associated '%s' header parameter as required by https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11. Header: %s
What it means
RFC 7515 Section 4.1.11 requires that every header name listed in the 'crit' set also be present as an actual header parameter in the protected header, so its value is integrity protected. jjwt iterates the crit set during parse and throws MalformedJwtException if any referenced name is absent from the header.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:445
}
// ----- 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);
throw new MalformedJwtException(msg);
}
if (!supportedCrit.contains(name)) {
String msg = String.format(CRIT_UNSUPPORTED_MSG, name, name, header);
throw new UnsupportedJwtException(msg);
}
}
}
// =============== Payload =================
final CharSequence payloadToken = tokenized.getPayload();
Payload payload;
boolean integrityVerified = false; // only true after successful signature verification or AEAD decryption
// check if b64 extension enabled:
final boolean payloadBase64UrlEncoded = !(header instanceof JwsHeader) || ((JwsHeader) header).isPayloadEncoded();
if (payloadBase64UrlEncoded) {
// standard encoding, so decode it:
byte[] data = decode(payloadToken, "payload");View on GitHub (pinned to fb71496164)
Solutions
- Add the referenced header parameter to the protected header (e.g. include "b64":false when crit lists "b64").
- Remove the name from the crit set if the extension is not actually used.
- Fix the token producer so crit always mirrors the actual header contents.
- At ingestion, validate crit vs header keys before parsing to give callers clearer feedback.
Example fix
// before
{"alg":"HS256","crit":["b64"]}
// after
{"alg":"HS256","b64":false,"crit":["b64"]} Defensive patterns
Strategy: validation
Validate before calling
java.util.Map<String,Object> h = parseHeaderJson(token);
@SuppressWarnings("unchecked") java.util.List<String> crit = (java.util.List<String>) h.get("crit");
if (crit != null) for (String n : crit) if (!h.containsKey(n)) throw new IllegalArgumentException("crit references missing header: " + n); Try / catch
try { return parser.parse(token); }
catch (io.jsonwebtoken.MalformedJwtException e) { throw new InvalidTokenException("crit references absent header", e); } Prevention
- Keep crit and the actual header keys in sync at the producer
- Don't strip headers from forwarded tokens without updating crit
- Add producer-side unit tests asserting crit ⊆ header keys
- Decode headers in CI when changing extension code
When it happens
Trigger: Parsing a JWS/JWE whose header declares e.g. "crit":["b64"] (or any custom name) without a corresponding "b64":... entry in the same header.
Common situations: Hand-built headers where crit was added but the referenced parameter forgotten; generic extension frameworks adding crit entries unconditionally; token transformation/forwarding code that strips headers but keeps crit.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- JWS header does not contain a required 'alg' (Algorithm) hea
- Unsecured JWSs (those with an alg header value of 'none') ma
- The JWS header references signature algorithm 'none' yet the
- Protected Header crit set references unsupported header name
- Unexpected content JWS.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/8dda0803fd1d29c4.
Report an issue: GitHub.