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

  1. Add the referenced header parameter to the protected header (e.g. include "b64":false when crit lists "b64").
  2. Remove the name from the crit set if the extension is not actually used.
  3. Fix the token producer so crit always mirrors the actual header contents.
  4. 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

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


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/8dda0803fd1d29c4. Report an issue: GitHub.