{"record":{"id":"161da12878fccbeb","repo":"jwtk/jjwt","slug":"the-jwe-header-references-key-management-algorithm","errorCode":null,"errorMessage":"The JWE header references key management algorithm '%s' but the compact JWE string is missing the required AAD authentication tag.","messagePattern":"The JWE header references key management algorithm '(.+?)' but the compact JWE string is missing the required AAD authentication tag\\.","errorType":"exception","errorClass":"MalformedJwtException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java","lineNumber":426,"sourceCode":"            if (tokenized instanceof TokenizedJwe) {\n                throw new MalformedJwtException(JWE_NONE_MSG);\n            }\n            // Unsecured JWTs are disabled by default per the RFC:\n            if (!this.unsecured) {\n                String msg = UNSECURED_DISABLED_MSG_PREFIX + header;\n                throw new UnsupportedJwtException(msg);\n            }\n            if (hasDigest) {\n                throw new MalformedJwtException(JWS_NONE_SIG_MISMATCH_MSG);\n            }\n            if (header.containsKey(DefaultProtectedHeader.CRIT.getId())) {\n                String msg = String.format(CRIT_UNSECURED_MSG, header);\n                throw new MalformedJwtException(msg);\n            }\n        } else if (!hasDigest) { // something other than 'none'.  Must have a digest component:\n            String fmt = tokenized instanceof TokenizedJwe ? MISSING_JWE_DIGEST_MSG_FMT : MISSING_JWS_DIGEST_MSG_FMT;\n            String msg = String.format(fmt, alg);\n            throw new MalformedJwtException(msg);\n        }\n        // ----- crit assertions -----\n        if (header instanceof ProtectedHeader) {\n            Set<String> crit = Collections.nullSafe(((ProtectedHeader) header).getCritical());\n            Set<String> supportedCrit = this.critical;\n            String b64Id = DefaultJwsHeader.B64.getId();\n            if (!unencodedPayload.isEmpty() && !this.critical.contains(b64Id)) {\n                // The application developer explicitly indicates they're using a B64 payload, so\n                // ensure that the B64 crit header is supported, even if they forgot to configure it on the\n                // parser builder:\n                supportedCrit = new LinkedHashSet<>(Collections.size(this.critical) + 1);\n                supportedCrit.add(DefaultJwsHeader.B64.getId());\n                supportedCrit.addAll(this.critical);\n            }\n            // assert any values per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11:\n            for (String name : crit) {\n                if (!header.containsKey(name)) {\n                    String msg = String.format(CRIT_MISSING_MSG, name, name, header);","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java#L408-L444","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before: truncated JWE (missing auth tag)\nString jwe = header + \".\" + key + \".\" + iv + \".\" + ciphertext;\n\n// after: full 5-part JWE\nString jwe = header + \".\" + key + \".\" + iv + \".\" + ciphertext + \".\" + tag;\n// or generate: Jwts.builder().claims(c).encryptWith(k, alg, enc).compact();","handlingStrategy":"validation","validationCode":"String[] parts = jwe.split(\"\\\\.\", -1);\nif (parts.length != 5 || parts[4].isEmpty()) throw new IllegalArgumentException(\"JWE must have 5 non-empty parts including the auth tag\");","typeGuard":null,"tryCatchPattern":"try { return parser.parse(jwe); }\ncatch (io.jsonwebtoken.MalformedJwtException e) { throw new InvalidTokenException(\"JWE missing authentication tag\", e); }","preventionTips":["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"],"tags":["jwe","jwt","missing-auth-tag","malformed-token"],"backgroundTag":"invalid-argument-format","analyzedSha":"fb71496164c71442d08adec4571d9616ed5e1b8d","analyzedAt":"2026-09-09T00:33:09.982Z","contentChangedAt":"2026-09-09T00:33:09.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}