{"record":{"id":"6cb76035cdd8cc8b","repo":"jwtk/jjwt","slug":"compact-jwe-strings-must-always-contain-a-payload","errorCode":null,"errorMessage":"Compact JWE strings MUST always contain a payload (ciphertext).","messagePattern":"Compact JWE strings MUST always contain a payload \\(ciphertext\\)\\.","errorType":"exception","errorClass":"MalformedJwtException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java","lineNumber":487,"sourceCode":"            if (Strings.hasText(payloadToken)) {\n                // we need to verify what was in the token, otherwise it'd be a security issue if we ignored it\n                // and assumed the (likely safe) unencodedPayload value instead:\n                payload = new Payload(payloadToken, header.getContentType());\n            } else {\n                //no payload token (a detached payload), so we need to ensure that they've specified the payload value:\n                if (unencodedPayload.isEmpty()) {\n                    String msg = String.format(B64_MISSING_PAYLOAD, header);\n                    throw new SignatureException(msg);\n                }\n                // otherwise, use the specified payload:\n                payload = unencodedPayload;\n            }\n        }\n\n        if (tokenized instanceof TokenizedJwe && payload.isEmpty()) {\n            // Only JWS payload can be empty per https://github.com/jwtk/jjwt/pull/540\n            String msg = \"Compact JWE strings MUST always contain a payload (ciphertext).\";\n            throw new MalformedJwtException(msg);\n        }\n\n        byte[] iv = null;\n        byte[] digest = null; // either JWE AEAD tag or JWS signature after Base64Url-decoding\n        if (tokenized instanceof TokenizedJwe) {\n\n            TokenizedJwe tokenizedJwe = (TokenizedJwe) tokenized;\n            JweHeader jweHeader = Assert.stateIsInstance(JweHeader.class, header, \"Not a JweHeader. \");\n\n            // Ensure both an 'alg' and 'enc' header value exists and is supported before spending time/effort\n            // base64Url-decoding anything:\n            final AeadAlgorithm encAlg = this.encAlgs.apply(jweHeader);\n            Assert.stateNotNull(encAlg, \"JWE Encryption Algorithm cannot be null.\");\n            @SuppressWarnings(\"rawtypes\") final KeyAlgorithm keyAlg = this.keyAlgs.apply(jweHeader);\n            Assert.stateNotNull(keyAlg, \"JWE Key Algorithm cannot be null.\");\n\n            byte[] cekBytes = Bytes.EMPTY; //ignored unless using an encrypted key algorithm\n            CharSequence base64Url = tokenizedJwe.getEncryptedKey();","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java#L469-L505","documentation":"Unlike JWS payloads, which may legally be empty, a compact JWE must always contain ciphertext in its payload segment. jjwt throws MalformedJwtException when a TokenizedJwe has an empty payload, because encryption of nothing cannot be represented in compact serialization here.","triggerScenarios":"Calling parse() on a JWE compact string whose fourth segment (ciphertext) is empty — e.g. header.key.iv..tag or a truncated string.","commonSituations":"Attempting to 'encrypt' an empty body and producing a malformed token; truncation or corruption removing the ciphertext segment; copying 4 of 5 segments; custom serializers mishandling empty claims maps.","solutions":["Re-issue the JWE with non-empty content (an empty claims map still produces valid ciphertext).","Verify the compact string has all five non-empty JWE segments before parsing.","Fix the producer so it never serializes an empty ciphertext segment; if there is truly nothing to encrypt, use a signed JWS instead.","Check for transport truncation if the token appears cut off."],"exampleFix":"// before: JWE with empty ciphertext\nString jwe = h + \".\" + k + \".\" + iv + \".\" + \"\" + \".\" + tag;\n\n// after: always encrypt something (e.g. empty claims map)\nString jwe = Jwts.builder().claims(Collections.emptyMap())\n    .encryptWith(key, Jwts.KEY.A128KW, Jwts.ENC.A128GCM).compact();","handlingStrategy":"validation","validationCode":"String[] parts = jwe.split(\"\\\\.\", -1);\nif (parts.length == 5 && parts[3].isEmpty()) throw new IllegalArgumentException(\"JWE ciphertext segment is empty\");","typeGuard":null,"tryCatchPattern":"try { return parser.parse(jwe); }\ncatch (io.jsonwebtoken.MalformedJwtException e) { throw new InvalidTokenException(\"JWE payload empty\", e); }","preventionTips":["Never serialize empty ciphertext; encrypt at least an empty claims map","Use Jwts.builder().encryptWith(...) instead of manual serialization","Validate all five JWE segments are non-empty before parsing","Use a signed JWS if you have no content to encrypt"],"tags":["jwe","jwt","empty-payload","malformed-token"],"backgroundTag":"empty-required-field","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"}