jwtk/jjwt · error · io.jsonwebtoken.security.InvalidKeyException

Either a Key instance or a kty value is required to create a

Error message

Either a Key instance or a kty value is required to create a JWK.

What it means

DispatchingJwkFactory.assertKeyOrKeyType enforces that to create a JWK you must supply either a java.security.Key instance or a kty (key type) value in the JwkContext. With neither, there is no way to select a family-specific JwkFactory, so it throws InvalidKeyException.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/DispatchingJwkFactory.java:77

    @Override
    public JwkContext<Key> newContext(JwkContext<?> src, Key key) {
        Assert.notNull(src, "JwkContext cannot be null.");
        String kty = src.getType();
        assertKeyOrKeyType(key, kty);
        for (FamilyJwkFactory<Key, ?> factory : this.factories) {
            if (factory.supports(key) || factory.supports(src)) {
                JwkContext<Key> ctx = factory.newContext(src, key);
                return Assert.notNull(ctx, "FamilyJwkFactory implementation cannot return null JwkContexts.");
            }
        }
        throw noFamily(key, kty);
    }

    private static void assertKeyOrKeyType(Key key, String kty) {
        if (key == null && !Strings.hasText(kty)) {
            String msg = "Either a Key instance or a " + AbstractJwk.KTY + " value is required to create a JWK.";
            throw new InvalidKeyException(msg);
        }
    }

    @Override
    public Jwk<Key> createJwk(JwkContext<Key> ctx) {

        Assert.notNull(ctx, "JwkContext cannot be null.");

        final Key key = ctx.getKey();
        final String kty = Strings.clean(ctx.getType());
        assertKeyOrKeyType(key, kty);

        for (FamilyJwkFactory<Key, ?> factory : this.factories) {
            if (factory.supports(ctx)) {
                String algFamilyId = Assert.hasText(factory.getId(), "factory id cannot be null or empty.");
                if (kty == null) {
                    ctx.setType(algFamilyId); //ensure the kty is available for the rest of the creation process
                }

View on GitHub (pinned to fb71496164)

Solutions

  1. Set a Key on the builder: Jwks.builder().setKey(key).build().
  2. Or set the key type explicitly: Jwks.builder().setKty("RSA").put(...).build().
  3. Ensure context values are populated before calling createJwk when building contexts manually.

Example fix

// before
Jwk jwk = Jwks.builder().put("n", n).put("e", e).build();
// after
Jwk jwk = Jwks.builder().setKty("RSA").put("n", n).put("e", e).build();
Defensive patterns

Strategy: validation

Validate before calling

if (key == null && (kty == null || kty.trim().isEmpty())) {
    throw new IllegalArgumentException("Provide either a Key or a kty value to build a JWK");
}

Try / catch

try {
    Jwk<?> jwk = Jwks.builder().setKey(key).build();
} catch (io.jsonwebtoken.security.InvalidKeyException e) {
    // set kty or key before building
}

Prevention

When it happens

Trigger: Calling Jwks.builder() without .setKey(...) and without .setKty(...); building a JwkContext programmatically with an empty context and calling createJwk.

Common situations: Fluent Jwks.builder() chains where the developer forgot to set the key or kty; dynamic JWK creation where values are conditionally populated and both key and kty end up absent.

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/7e18f29629d8265e. Report an issue: GitHub.