jwtk/jjwt · error · io.jsonwebtoken.MalformedJwtException
Unable to read compact JWT: ${e.getMessage()}
Error message
Unable to read compact JWT: ${e.getMessage()} What it means
JwtTokenizer.read wraps any IOException from reading the compact JWT's character stream in a MalformedJwtException with this message. The library treats an I/O failure while reading the token as a malformed-token condition, chaining the original IOException as the cause.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/JwtTokenizer.java:38
import io.jsonwebtoken.lang.Assert;
import io.jsonwebtoken.lang.Strings;
import java.io.IOException;
import java.io.Reader;
public class JwtTokenizer {
static final char DELIMITER = '.';
private static final String DELIM_ERR_MSG_PREFIX = "Invalid compact JWT string: Compact JWSs must contain " +
"exactly 2 period characters, and compact JWEs must contain exactly 4. Found: ";
private static int read(Reader r, char[] buf) {
try {
return r.read(buf);
} catch (IOException e) {
String msg = "Unable to read compact JWT: " + e.getMessage();
throw new MalformedJwtException(msg, e);
}
}
@SuppressWarnings("unchecked")
public <T extends TokenizedJwt> T tokenize(Reader reader) {
Assert.notNull(reader, "Reader argument cannot be null.");
CharSequence protectedHeader = Strings.EMPTY; //Both JWS and JWE
CharSequence body = Strings.EMPTY; //JWS payload or JWE Ciphertext
CharSequence encryptedKey = Strings.EMPTY; //JWE only
CharSequence iv = Strings.EMPTY; //JWE only
CharSequence digest = Strings.EMPTY; //JWS Signature or JWE AAD Tag
int delimiterCount = 0;
char[] buf = new char[4096];
int len = 0;
StringBuilder sb = new StringBuilder(4096);View on GitHub (pinned to fb71496164)
Solutions
- Pass the compact JWT as a String instead of a Reader to avoid streaming I/O
- Check the cause (IOException) to fix the underlying source: reopen the stream, check connectivity, or verify file access
- Catch MalformedJwtException (or its IOException cause) and retry once if the source is transient
Example fix
// before parser.parseClaimsJws(new InputStreamReader(socket.getInputStream())); // after String token = readFully(socket.getInputStream()); // no IOException mid-parse parser.parseClaimsJws(token);
Defensive patterns
Strategy: try-catch
Try / catch
try {
Jws<Claims> jws = parser.parseClaimsJws(token);
} catch (MalformedJwtException e) {
Throwable cause = e.getCause();
if (cause instanceof IOException) retryOrReopenStream();
} Prevention
- Prefer passing the JWT String directly rather than streaming via Reader
- Ensure stream sources are fully readable and closed properly
- Check connectivity/file access when parsing from external sources
When it happens
Trigger: Calling Jwts.parser().parse(...) or parseClaimsJws(...) with a Reader whose underlying source (socket, file, stream) throws IOException mid-read.
Common situations: Reading tokens from a network stream that was closed or reset; parsing from a file/stream already consumed or with encoding issues; passing an Reader backed by a failing input source instead of the token String.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- Compact JWT strings MUST always have a Base64Url protected h
- JWS header does not contain a required 'alg' (Algorithm) hea
- JWEs do not support key management alg header value 'none' p
- The JWS header references signature algorithm 'none' yet the
- Unsecured JWSs (those with an alg header value of 'none') ma
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/744d3ef70844116a.
Report an issue: GitHub.