jwtk/jjwt · error · IllegalStateException

'b64' Unencoded payload option has been specified, but paylo

Error message

'b64' Unencoded payload option has been specified, but payload is empty.

What it means

Thrown from sign() (invoked by compact()) when the 'b64':false unencoded-payload JWS option is set but the payload produces zero bytes. With unencoded payloads the signature must be computed over the raw payload, so an empty payload makes the detached/unencoded construction impossible and the builder fails with an IllegalStateException.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java:619

        byte[] signature;
        try {
            SecureRequest<InputStream, Key> request = new DefaultSecureRequest<>(signingInput, provider, secureRandom, key);
            signature = signFunction.apply(request);

            // now that we've calculated the signature, if using the b64 extension, and the payload is
            // attached ('non-detached'), we need to include it in the jws before the signature token.
            // (Note that if encodePayload is true, the payload has already been written to jws at this point, so
            // we only need to write if encodePayload is false and the payload is attached):
            if (!this.encodePayload) {
                if (!payload.isCompressed() // don't print raw compressed bytes
                        && (payload.isClaims() || payload.isString())) {
                    // now add the payload to the jws output:
                    Streams.copy(payloadStream, jws, new byte[8192], "Unable to copy attached Payload InputStream.");
                }
                if (payloadStream instanceof CountingInputStream && ((CountingInputStream) payloadStream).getCount() <= 0) {
                    String msg = "'b64' Unencoded payload option has been specified, but payload is empty.";
                    throw new IllegalStateException(msg);
                }
            }
        } finally {
            Streams.reset(payloadStream);
        }

        // ----- separator -----
        jws.write(DefaultJwtParser.SEPARATOR_CHAR);

        // ----- signature -----
        encodeAndWrite("JWS Signature", signature, jws);

        return Strings.utf8(jws.toByteArray());
    }

    private String unprotected(final Payload content) {

        Assert.stateNotNull(content, "Content argument cannot be null.");

View on GitHub (pinned to fb71496164)

Solutions

  1. Ensure setContent(...) or setClaims(...) provides a non-empty payload before compact()
  2. Remove the b64(false) call if you do not actually need unencoded (detached) payloads
  3. Validate that the content InputStream/byte[] is non-empty before building

Example fix

// before
String jws = Jwts.builder().b64(false).compact(); // payload never set
// after
String jws = Jwts.builder().b64(false).setContent(payloadBytes).compact();
Defensive patterns

Strategy: validation

Validate before calling

if (unencodedPayload && (content == null || content.length == 0)) {
    throw new IllegalArgumentException("Unencoded-payload JWS requires non-empty content");
}

Try / catch

try {
    return builder.compact();
} catch (IllegalStateException e) {
    if (e.getMessage().contains("payload is empty")) {
        throw new TokenBuildException("b64:false requires non-empty payload", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling b64(false) (unencoded payload option) on a JwtBuilder while the payload is empty — i.e. no content/claims set, or content that serializes to zero bytes — then compact().

Common situations: Detached-content JWS flows where the caller forgot to attach the payload before compacting; conditional code paths that skip setContent(); misuse of b64(false) copied from unencoded-payload examples.

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


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