jwtk/jjwt · error · io.jsonwebtoken.MalformedJwtException

Invalid compact JWT string: Compact JWSs must contain…

Error message

Invalid compact JWT string: Compact JWSs must contain exactly 2 period characters, and compact JWEs must contain exactly 4.  Found: ${delimiterCount}

What it means

A compact JWS must contain exactly 2 period delimiters and a compact JWE exactly 4. After tokenizing, JwtTokenizer counts the delimiters and throws MalformedJwtException if the count is neither 2 nor 4, meaning the string is not a structurally valid compact JWT at all.

Solutions

  1. Verify the token source: count '.' occurrences (2 for JWS, 4 for JWE) before parsing
  2. Log/inspect the raw incoming token to find where it gets truncated or mangled
  3. Distinguish token types in the client and route JWEs to parseEncryptedJws and JWSs to parseClaimsJws
  4. Catch MalformedJwtException and reject with 'malformed token' instead of surfacing a 500

Example fix

// before
String token = req.getHeader("Authorization").substring(7);
parser.parseClaimsJws(token); // token truncated
// after
String token = req.getHeader("Authorization").substring(7);
long dots = token.chars().filter(c -> c == '.').count();
if (dots != 2 && dots != 4) throw new MalformedJwtException("bad token shape");
parser.parseClaimsJws(token);
Defensive patterns

Strategy: validation

Validate before calling

long dots = token == null ? -1 : token.chars().filter(c -> c == '.').count();
if (dots != 2 && dots != 4) throw new MalformedJwtException("Not a compact JWT: " + dots + " dots");

Try / catch

try {
    Jws<Claims> jws = parser.parseClaimsJws(token);
} catch (MalformedJwtException e) {
    respond(400, "Token is not a valid compact JWT");
}

Prevention

When it happens

Trigger: Passing a truncated, concatenated, corrupted, or completely non-JWT string to parse/parseClaimsJws/parseEncryptedJws — e.g. an empty fragment, a token with a period added/removed, or a base64 blob that isn't a JWT.

Common situations: String truncation from fixed-size storage columns or log capture; sending the wrong token type (opaque session id) to a JWT parser; tokens mangled by URL encoding/decoding; clients sending 'null' or 'undefined' literal strings.

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


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

Appendix: source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/JwtTokenizer.java:102

                            body = Strings.EMPTY; //clear out value set for JWS
                            iv = token;
                            break;
                        case 3:
                            body = token;
                            break;
                    }

                    delimiterCount++;
                    sb.setLength(0);
                } else {
                    sb.append(c);
                }
            }
        }

        if (delimiterCount != 2 && delimiterCount != 4) {
            String msg = DELIM_ERR_MSG_PREFIX + delimiterCount;
            throw new MalformedJwtException(msg);
        }

        if (sb.length() > 0) {
            digest = sb.toString();
        }

        if (delimiterCount == 2) {
            return (T) new DefaultTokenizedJwt(protectedHeader, body, digest);
        }

        return (T) new DefaultTokenizedJwe(protectedHeader, body, digest, encryptedKey, iv);
    }
}

View on GitHub (pinned to fb71496164)