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
- Ensure setContent(...) or setClaims(...) provides a non-empty payload before compact()
- Remove the b64(false) call if you do not actually need unencoded (detached) payloads
- 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
- Only call b64(false) when you truly need detached/unencoded payloads
- Assert content is non-empty before compact()
- Set content immediately after b64(false) so it cannot be skipped by a code path
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
- Unexpected content JWS.
- Unexpected Claims JWS.
- Both 'signWith' and 'encryptWith' cannot be specified. Choos
- Encrypted JWTs must have either 'claims' or non-empty 'conte
- JWS header does not contain a required 'alg' (Algorithm) hea
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/ffea2cc0e2c2e66f.
Report an issue: GitHub.