jwtk/jjwt · error · IncorrectClaimException

Expected <expectedClaimName> claim to be: <expectedClaimValu

Error message

Expected <expectedClaimName> claim to be: <expectedClaimValue>, but was: <actualClaimValue>.

What it means

Thrown as IncorrectClaimException when a required claim's value does not exactly equal the expected value passed to require(claimName, expectedValue) for a scalar (non-Collection, non-Date) expected value. This is the equality-failure branch of JJWT's claim requirement validation, after the missing-claim and collection-membership checks.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java:771

                } else {
                    msg += ": " + expectedClaimValue;
                }
                throw new MissingClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);
            } else if (expectedClaimValue instanceof Collection) {
                Collection<?> expectedValues = (Collection<?>) expectedClaimValue;
                Collection<?> actualValues = actualClaimValue instanceof Collection ? (Collection<?>) actualClaimValue :
                        Collections.setOf(actualClaimValue);
                for (Object expectedValue : expectedValues) {
                    if (!Collections.contains(actualValues.iterator(), expectedValue)) {
                        String msg = String.format(MISSING_EXPECTED_CLAIM_VALUE_MESSAGE_TEMPLATE,
                                expectedValue, expectedClaimName, actualValues);
                        throw new IncorrectClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);
                    }
                }
            } else if (!expectedClaimValue.equals(actualClaimValue)) {
                String msg = String.format(INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE,
                        expectedClaimName, expectedClaimValue, actualClaimValue);
                throw new IncorrectClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);
            }
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public <T> T parse(CharSequence compact, JwtHandler<T> handler) {
        return parse(compact, Payload.EMPTY).accept(handler);
    }

    private Jwt<?, ?> parse(CharSequence compact, Payload unencodedPayload) {
        Assert.hasText(compact, "JWT String argument cannot be null or empty.");
        return parse(new CharSequenceReader(compact), unencodedPayload);
    }

    @Override
    public Jwt<Header, byte[]> parseContentJwt(CharSequence jwt) {
        return parse(jwt).accept(Jwt.UNSECURED_CONTENT);

View on GitHub (pinned to fb71496164)

Solutions

  1. Verify the exact expected value against the token payload (decode the claims section) and fix require(...).
  2. Fix the issuer to emit the expected claim value.
  3. Normalize issuer/audience strings on both sides (trailing slashes, case) and re-deploy.
  4. If multiple acceptable values exist, use require(name, collectionOfAcceptedValues) instead of a scalar.

Example fix

// before
Jwts.parser().require("iss", "https://auth.example.com/").verifyWith(key).build().parse(jwt); // token iss="https://auth.example.com"
// after
Jwts.parser().require("iss", "https://auth.example.com").verifyWith(key).build().parse(jwt);
Defensive patterns

Strategy: validation

Validate before calling

io.jsonwebtoken.Claims c = Jwts.parser().build().parseUnsecuredClaims(jwt).getPayload();
if (!"https://auth.example.com".equals(c.get("iss"))) {
    throw new IllegalStateException("Unexpected iss: " + c.get("iss"));
}

Try / catch

try {
    claims = Jwts.parser().requireIssuer("https://auth.example.com").verifyWith(key)
        .build().parseSignedClaims(jwt).getPayload();
} catch (io.jsonwebtoken.IncorrectClaimException e) {
    // reject: claim value mismatch; log e.getClaimName() and expected/actual
}

Prevention

When it happens

Trigger: parser.require("iss", "https://issuer.example.com") or require("sub", userId) where the claim is present but its value differs from the expected one (uses Object.equals).

Common situations: Wrong issuer URL or trailing-slash differences in 'iss'; token signed for a different subject/user; environment-specific issuer values (staging vs prod); case sensitivity mismatches ('APP' vs 'app').

Related errors


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