jwtk/jjwt · error · IllegalArgumentException

${msgPrefix}${type} key must be an instance of ${clazz.getNa

Error message

${msgPrefix}${type} key must be an instance of ${clazz.getName()}. Type found: ${key.getClass().getName()}

What it means

KeyPairs.assertKey verifies that a key handed to a KeyPairBuilder (via getKey) is an instance of the expected key class (e.g. RSAPrivateKey, ECPublicKey). If not, it throws IllegalArgumentException stating the required class and the actual type found. The 'private'/'public' prefix in the message identifies which half of the pair failed the check.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/KeyPairs.java:57

        }
    }

    public static <K> K getKey(KeyPair pair, Class<K> clazz) {
        Assert.notNull(pair, "KeyPair cannot be null.");
        String prefix = familyPrefix(clazz) + "KeyPair ";
        boolean isPrivate = PrivateKey.class.isAssignableFrom(clazz);
        Key key = isPrivate ? pair.getPrivate() : pair.getPublic();
        return assertKey(key, clazz, prefix);
    }

    public static <K> K assertKey(Key key, Class<K> clazz, String msgPrefix) {
        Assert.notNull(key, "Key argument cannot be null.");
        Assert.notNull(clazz, "Class argument cannot be null.");
        String type = key instanceof PrivateKey ? "private" : "public";
        if (!clazz.isInstance(key)) {
            String msg = msgPrefix + type + " key must be an instance of " + clazz.getName() +
                ". Type found: " + key.getClass().getName();
            throw new IllegalArgumentException(msg);
        }
        return clazz.cast(key);
    }
}

View on GitHub (pinned to fb71496164)

Solutions

  1. Read the required class name in the message and ensure the key implements it (e.g. java.security.interfaces.RSAPrivateKey).
  2. Generate/load keys with the correct algorithm: KeyPairGenerator.getInstance("RSA") for RSA, "EC" for EC.
  3. Swap the arguments if private/public were transposed.
  4. If the key is from a provider wrapper, extract the underlying key via its getFormat/encoding or use the provider's native interface.

Example fix

// before
KeyPair kp = KeyPairGenerator.getInstance("EC").generateKeyPair();
Jwks.builder().keyPair(kp).privateKey(kp.getPublic()); // wrong slot

// after
Jwks.builder().keyPair(kp)
    .privateKey((ECPrivateKey) kp.getPrivate())
    .publicKey((ECPublicKey) kp.getPublic()).build();
Defensive patterns

Strategy: type-guard

Validate before calling

boolean isRsaKeyPair(KeyPair kp) {
    return kp.getPublic() instanceof java.security.interfaces.RSAPublicKey
        && kp.getPrivate() instanceof java.security.interfaces.RSAPrivateKey;
}

Type guard

if (key instanceof java.security.interfaces.RSAPrivateKey rsa) {
    builder.privateKey(rsa);
} else {
    throw new IllegalArgumentException("Expected RSAPrivateKey, got " + key.getClass().getName());
}

Try / catch

try {
    jwk = builder.build();
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("key must be an instance of")) {
        // regenerate or cast keys to the expected interface
    } else throw e;
}

Prevention

When it happens

Trigger: Calling KeyPairs (e.g. Jwks.builder().keyPair(...).privateKey(key) or .publicKey(key)) with a key of the wrong type/interface, such as passing a generic PrivateKey or a DH key where an RSA key is required, or swapping private/public arguments.

Common situations: Passing keys loaded from a keystore under the wrong algorithm (EC key where RSA expected); using a PKCS11 provider key that implements a different interface; accidentally passing the public key to the privateKey slot; keys of unsupported algorithms (Edwards vs EC confusion).

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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