jwtk/jjwt · error · IllegalStateException
Encrypted JWTs must have either 'claims' or non-empty 'conte
Error message
Encrypted JWTs must have either 'claims' or non-empty 'content'.
What it means
A JWE's payload is always encoded and protected, but it must contain something: when building an encrypted JWT the builder requires either claims or non-empty content. If both are absent, compact() throws IllegalStateException because an empty encrypted payload is meaningless and not representable.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java:492
}
}
@Override
public String compact() {
final boolean jwe = this.enc != null;
if (jwe && signFunction != null) {
String msg = "Both 'signWith' and 'encryptWith' cannot be specified. Choose either one.";
throw new IllegalStateException(msg);
}
Payload payload = Assert.stateNotNull(this.payload, "Payload instance null, internal error");
final Claims claims = this.claimsBuilder.build();
if (jwe && payload.isEmpty() && Collections.isEmpty(claims)) { // JWE payload can never be empty:
String msg = "Encrypted JWTs must have either 'claims' or non-empty 'content'.";
throw new IllegalStateException(msg);
} // otherwise JWS and Unprotected JWT payloads can be empty
if (!payload.isEmpty() && !Collections.isEmpty(claims)) {
throw new IllegalStateException("Both 'content' and 'claims' cannot be specified. Choose either one.");
}
if (this.serializer == null) { // try to find one based on the services available
//noinspection unchecked
json(Services.get(Serializer.class));
}
if (!Collections.isEmpty(claims)) { // normalize so we have one object to deal with:
payload = new Payload(claims);
}
if (compressionAlgorithm != null && !payload.isEmpty()) {
payload.setZip(compressionAlgorithm);
this.headerBuilder.put(DefaultHeader.COMPRESSION_ALGORITHM.getId(), compressionAlgorithm.getId());
}View on GitHub (pinned to fb71496164)
Solutions
- Set claims: Jwts.builder().claims().subject(...).and().encryptWith(...) before compact().
- Or set non-empty content: builder.content(byte[]/String payload).
- Add an assertion before compact(): ensure the builder has claims or content when encryption is configured.
- Catch IllegalStateException and treat it as a programming/configuration bug in the token-building path.
Example fix
// before
Jwts.builder().encryptWith(pubKey, alg, enc).compact(); // empty
// after
Jwts.builder().claims().subject("user").and().encryptWith(pubKey, alg, enc).compact(); Defensive patterns
Strategy: validation
Validate before calling
if (encrypting && claims == null && (content == null || content.length == 0)) throw new IllegalStateException("JWE needs claims or non-empty content"); Try / catch
try { return builder.compact(); } catch (IllegalStateException e) { /* JWE built without payload */ } Prevention
- Always set claims or content before compact() when using encryptWith
- Never reuse builder instances across requests without resetting payload state
- Add unit tests asserting compact() output is non-empty for encrypted tokens
When it happens
Trigger: Calling compact() after encryptWith(...) without calling claims(...)/setClaims(...) or content(...) with non-empty data.
Common situations: Builders reused across requests where the payload-setting branch was skipped; conditional logic that only sets claims sometimes; refactors that dropped the claims call while keeping encryptWith.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Compact JWE strings MUST always contain a payload (ciphertex
- Unexpected content JWE.
- Unexpected Claims JWE.
- PrivateKeys may not be used to encrypt data. PublicKeys are
- Payload encoding may not be disabled for s, only JWSs.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/8c58fd73e737bc3c.
Report an issue: GitHub.