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
- Read the required class name in the message and ensure the key implements it (e.g. java.security.interfaces.RSAPrivateKey).
- Generate/load keys with the correct algorithm: KeyPairGenerator.getInstance("RSA") for RSA, "EC" for EC.
- Swap the arguments if private/public were transposed.
- 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
- Generate keys with KeyPairGenerator using the algorithm matching your intended JWK family (RSA/EC/EdDSA).
- Never pass generic Key/PrivateKey references; hold typed interfaces (RSAPublicKey, ECPrivateKey).
- Assert key types in unit tests before building JWKs.
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
- ${message}Object of class [${objClassName}] must be an insta
- ${message}${subType} is not assignable to ${superType}
- Source is not an array:
- Invalid claims: + t.getMessage()
- JWT Claim '<expectedClaimName>' was expected to be a Date, b
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/e3dc21bd15ab1a9b.
Report an issue: GitHub.