jwtk/jjwt · error · IllegalStateException
Both 'signWith' and 'encryptWith' cannot be specified. Choos
Error message
Both 'signWith' and 'encryptWith' cannot be specified. Choose either one.
What it means
A compact JWT is either signed (JWS) or encrypted (JWE), never both simultaneously. Specifying both signWith and encryptWith on the same builder is ambiguous, so compact() throws IllegalStateException.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java:484
public JwtBuilder id(String jti) {
return claims().id(jti).and();
}
private void assertPayloadEncoding(String type) {
if (!this.encodePayload) {
String msg = "Payload encoding may not be disabled for " + type + "s, only JWSs.";
throw new IllegalArgumentException(msg);
}
}
@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));
}View on GitHub (pinned to fb71496164)
Solutions
- Choose one protection mode: remove signWith for encrypted tokens or remove encryptWith for signed ones.
- For sign-then-encrypt (nested JWT), build the inner signed JWS string first, then encrypt that string with a second builder: Jwts.builder().content(innerJws).encryptWith(...).compact().
- Refactor so a single code path/config decides signing vs encryption per builder.
- Catch IllegalStateException and report the mutually exclusive configuration.
Example fix
// before Jwts.builder().setClaims(c).signWith(key).encryptWith(pubKey, alg, enc).compact(); // after String jws = Jwts.builder().setClaims(c).signWith(key).compact(); String jwe = Jwts.builder().content(jws).encryptWith(pubKey, alg, enc).compact();
Defensive patterns
Strategy: validation
Validate before calling
if (signingConfigured && encryptingConfigured) throw new IllegalStateException("Choose either signWith or encryptWith"); Try / catch
try { return builder.compact(); } catch (IllegalStateException e) { /* builder has both sign and encrypt set */ } Prevention
- One builder, one protection mode
- For nested JWTs, chain two builders (sign inner, encrypt outer)
- Centralize token creation so signing and encryption are mutually exclusive config branches
When it happens
Trigger: Calling builder.signWith(...) and later builder.encryptWith(...) on the same builder instance, then compact(). Common with shared/configured builders where both security settings are applied.
Common situations: Nested/nested-JWT attempts done by chaining both calls instead of the documented nested JWT pattern; a builder configured by multiple code paths each assuming the other mode.
Related errors
- Unexpected content JWS.
- Unexpected Claims JWS.
- Unexpected content JWE.
- Unexpected Claims JWE.
- PrivateKeys may not be used to encrypt data. PublicKeys are
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/b69a325aad7af0c1.
Report an issue: GitHub.