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

  1. Fix token issuance so the required header id (e.g. kid) is always included
  2. If the id is genuinely optional, build the locator without the required message so missing values return null
  3. 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

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


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)