jwtk/jjwt · error · IllegalStateException
Both 'content' and 'claims' cannot be specified. Choose eith
Error message
Both 'content' and 'claims' cannot be specified. Choose either one.
What it means
This error is thrown by DefaultJwtBuilder.compact() when a caller has set both raw content (e.g. setContent(...)) and claims (e.g. setClaims(...)) on the builder. A JWT can have exactly one payload representation — either opaque string/byte content or a claims JSON object — so specifying both is ambiguous and rejected with an IllegalStateException before serialization.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java:496
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());
}
if (Strings.hasText(payload.getContentType())) {
// We retain the value from the content* calls to prevent accidental removal from
// header().empty() or header().delete callsView on GitHub (pinned to fb71496164)
Solutions
- Remove either the setContent(...) call or the setClaims(...)/claim(...) calls so only one payload source remains
- If reusing a builder, create a fresh builder per token (Jwts.builder()) instead of resetting fields
- Use claim() for standard business-logic tokens; use setContent() only for opaque/non-JSON payloads, never both
Example fix
// before
String jwt = Jwts.builder()
.setContent("raw-payload")
.claim("sub", "user")
.compact();
// after
String jwt = Jwts.builder()
.claim("sub", "user")
.compact(); Defensive patterns
Strategy: validation
Validate before calling
if (content != null && claims != null) {
throw new IllegalArgumentException("Set either content or claims, not both");
} Try / catch
try {
String jwt = builder.compact();
} catch (IllegalStateException e) {
if (e.getMessage().contains("Both 'content' and 'claims'")) {
throw new TokenBuildException("Conflicting payload configuration", e);
}
throw e;
} Prevention
- Use a fresh Jwts.builder() per token instead of reusing instances
- Decide per token type whether it is content-based or claims-based and enforce in a wrapper method
- Avoid wrapper APIs that call both setContent and claim on the same builder
When it happens
Trigger: Calling JwtBuilder.setContent()/setContentInputStream() and then setClaims()/claim() (or vice versa) on the same builder instance before compact(). Also happens when a builder is reused: claims set in one code path plus content set in another.
Common situations: Reusing a shared builder across methods; migrating code from content-based JWS to claims-based JWS while leaving the old setContent call in place; framework code that always calls claim(...) on a builder the user already populated with content.
Related errors
- Both 'signWith' and 'encryptWith' cannot be specified. Choos
- Unexpected unsecured Claims JWT.
- Unexpected content JWS.
- Unexpected Claims JWS.
- Unexpected content JWE.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/b2c391ef3c15f8ea.
Report an issue: GitHub.