jwtk/jjwt · error · io.jsonwebtoken.MalformedJwtException
${requiredMsg}
Error message
${requiredMsg} What it means
IdLocator reads an id value (e.g. kid header parameter) from the JWT and looks it up in a registry. When the value is missing or blank and a required message was supplied, it throws MalformedJwtException with that message. This means the token's header lacks a required identifier (such as kid) that key lookup depends on.
Solutions
- Fix token issuance so the required header id (e.g. kid) is always included
- If the id is genuinely optional, build the locator without the required message so missing values return null
- Catch MalformedJwtException and reject/refresh the token with a clear client-side error
Example fix
// before (token header)
{"alg":"HS256"} // missing kid
// after
{"alg":"HS256","kid":"key-2024-01"} Defensive patterns
Strategy: try-catch
Validate before calling
String kid = parsed.getHeader("kid");
if (kid == null || kid.isBlank()) throw new MalformedJwtException("token missing kid"); Try / catch
try {
Jws<Claims> jws = parser.parseClaimsJws(token);
} catch (MalformedJwtException e) {
respond(400, "Token is missing a required header identifier");
} Prevention
- Ensure the token issuer always sets kid when key lookup is id-based
- Decide explicitly whether the header id is required and configure the locator accordingly
- Monitor MalformedJwtException rates to detect issuer-side regressions
When it happens
Trigger: Parsing a JWT whose header omits the required id parameter (commonly 'kid') while the parser was built with a required key locator/registry (e.g. verifyWithKeyLocator or header-value-required configuration).
Common situations: Tokens issued by a provider that doesn't set kid being parsed by a consumer configured to require it; a token signed/serialized by different tooling that strips custom headers; rotation setups where new tokens must carry kid but old ones don't.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Compact JWE strings MUST always contain a payload…
- Compact JWT strings may not contain whitespace.
- Compact JWT strings MUST always have a Base64Url protected…
- Invalid Base64Url
- Invalid compact JWT string: Compact JWSs must contain…
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/2072e43190004069.
Report an issue: GitHub.
Appendix: source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/IdLocator.java:54
private final String requiredMsg;
public IdLocator(Parameter<String> param, Registry<String, R> registry, String algType, String behavior, String requiredExceptionMessage) {
this.param = Assert.notNull(param, "Header param cannot be null.");
this.registry = Assert.notNull(registry, "Registry cannot be null.");
this.algType = Assert.hasText(algType, "algType cannot be null or empty.");
this.behavior = Assert.hasText(behavior, "behavior cannot be null or empty.");
this.requiredMsg = Strings.clean(requiredExceptionMessage);
}
@Override
public R locate(Header header) {
Object val = header.get(this.param.getId());
String id = val != null ? val.toString() : null;
if (!Strings.hasText(id)) {
if (this.requiredMsg != null) { // a msg was provided, so the value is required:
throw new MalformedJwtException(requiredMsg);
}
return null; // otherwise header value not required, so short circuit
}
try {
return registry.forKey(id);
} catch (Exception e) {
StringBuilder sb = new StringBuilder("Unsupported ")
.append(DefaultHeader.nameOf(header))
.append(" ")
.append(this.param)
.append(" value '").append(id).append("'");
if (this.registry.isEmpty()) {
sb.append(": ")
.append(this.behavior)
.append(" is disabled (no ")
.append(this.algType)
.append(" algorithms have been configured)");View on GitHub (pinned to fb71496164)