jwtk/jjwt · error · UnsupportedJwtException
Cannot decrypt JWE payload: unable to locate key for JWE…
Error message
Cannot decrypt JWE payload: unable to locate key for JWE with header: + jweHeader
What it means
Before decrypting a JWE the parser calls the configured keyLocator to obtain the decryption key for the token's header; if the locator returns null, decryption is impossible and the parser throws UnsupportedJwtException naming the header so you can see which key the token expected.
Solutions
- Configure a key source: call decryptWith(SecretKey/PrivateKey) or keyLocator(Locator) on the JwtParserBuilder before parsing.
- Ensure the locator handles the token's kid/alg and returns a matching key, or throw with a clear message instead of returning null.
- Verify key rotation: if the kid is unknown, load archived keys or reject the token upstream.
- Log the JWE header (alg, kid, enc) and confirm your key store contains the corresponding key.
Example fix
// before
JwtParser parser = Jwts.parser().build();
// after
JwtParser parser = Jwts.parser()
.keyLocator(header -> keyStore.get(header.get("kid", String.class)))
.build(); Defensive patterns
Strategy: try-catch
Validate before calling
// ensure a key source is wired before parsing Objects.requireNonNull(myDecryptionKeyOrLocator, "JWE decryption key/locator must be configured");
Try / catch
try { parser.parse(jwe); } catch (UnsupportedJwtException e) { if (e.getMessage().startsWith("Cannot decrypt JWE payload")) { /* load/refresh key for kid, then retry */ } } Prevention
- Always configure decryptWith(...) or keyLocator(...) before parsing JWEs.
- Make key locators key-rotation aware (support archived kids) and throw instead of returning null on miss.
- Log the JWE header (kid/alg) when key lookup fails for faster diagnosis.
- Unit-test the locator against every kid your issuers can produce.
When it happens
Trigger: Parsing a JWE when no KeyLocator/Locator is configured, or the configured locator returns null for the token's alg/kid header (e.g. kid not present in the key store).
Common situations: Forgetting to call .keyLocator(...) or .decryptWith(key) on JwtParserBuilder, key rotation removing the kid the token references, multi-tenant lookup returning null for unknown issuers.
Related errors
- The ' ' JWE key algorithm did not return a decryption key…
- Both 'signWith' and 'encryptWith' cannot be specified…
- Ciphertext decryption failed: Authentication tag…
- Compact JWE string represents an encrypted key, but the key…
- Compact JWE strings MUST always contain a payload…
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/ae258fa576adf296.
Report an issue: GitHub.
Appendix: source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:543
// https://www.rfc-editor.org/rfc/rfc7516.html#section-5.1, Step 14.
ByteBuffer buf = StandardCharsets.US_ASCII.encode(Strings.wrap(base64UrlHeader));
final byte[] aadBytes = new byte[buf.remaining()];
buf.get(aadBytes);
InputStream aad = Streams.of(aadBytes);
base64Url = base64UrlDigest;
//guaranteed to be non-empty via the `alg` + digest check above:
Assert.hasText(base64Url, "JWE AAD Authentication Tag cannot be null or empty.");
digest = decode(base64Url, "JWE AAD Authentication Tag");
if (Bytes.isEmpty(digest)) {
String msg = "Compact JWE strings must always contain an AAD Authentication Tag.";
throw new MalformedJwtException(msg);
}
Key key = this.keyLocator.locate(jweHeader);
if (key == null) {
String msg = "Cannot decrypt JWE payload: unable to locate key for JWE with header: " + jweHeader;
throw new UnsupportedJwtException(msg);
}
if (key instanceof PublicKey) {
throw new InvalidKeyException(PUB_KEY_DECRYPT_MSG);
}
// extract key-specific provider if necessary;
Provider provider = ProviderKey.getProvider(key, this.provider);
key = ProviderKey.getKey(key); // this must be called after ProviderKey.getProvider
DecryptionKeyRequest<Key> request =
new DefaultDecryptionKeyRequest<>(cekBytes, provider, null, jweHeader, encAlg, key);
final SecretKey cek = keyAlg.getDecryptionKey(request);
if (cek == null) {
String msg = "The '" + keyAlg.getId() + "' JWE key algorithm did not return a decryption key. " +
"Unable to perform '" + encAlg.getId() + "' decryption.";
throw new IllegalStateException(msg);
}
// During decryption, the available Provider applies to the KeyAlgorithm, not the AeadAlgorithm, mostlyView on GitHub (pinned to fb71496164)