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 calls

View on GitHub (pinned to fb71496164)

Solutions

  1. Remove either the setContent(...) call or the setClaims(...)/claim(...) calls so only one payload source remains
  2. If reusing a builder, create a fresh builder per token (Jwts.builder()) instead of resetting fields
  3. 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

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


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/b2c391ef3c15f8ea. Report an issue: GitHub.