jwtk/jjwt · error · IllegalStateException
Both 'signWith' and 'encryptWith' cannot be specified…
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.
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
- 'b64' Unencoded payload option has been specified, but…
- Both 'content' and 'claims' cannot be specified. Choose…
- Compact JWE strings MUST always contain a payload…
- keys may not be used with ECDH-ES key agreement algorithms…
- Encrypted JWTs must have either 'claims' or non-empty…
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/b69a325aad7af0c1.
Report an issue: GitHub.
Appendix: 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)