jwtk/jjwt · error · IllegalArgumentException
Value must be ${articleFor(desired)} ${desired}, not ${artic
Error message
Value must be ${articleFor(desired)} ${desired}, not ${articleFor(jwkType)} ${jwkType}. What it means
This IllegalArgumentException is thrown by JwkConverter.applyFrom when a caller supplies a Jwk object instance, but it is not of the converter's desired type (e.g. the converter expects a PublicJwk but receives a PrivateJwk or SecretJwk). JwkConverter is used internally when parsing JWKs (e.g. for JWK Set members or key material in JWT headers), and the desired type enforces constraints like 'only public keys allowed'. The message names both the expected type and the actual JWK type so you can see exactly which kind of JWK was rejected.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/JwkConverter.java:127
} else if (PrivateJwk.class.isAssignableFrom(clazz)) {
nespace(sb).append("Private");
}
nespace(sb).append("JWK");
return sb.toString();
}
private IllegalArgumentException unexpectedIAE(Jwk<?> jwk) {
String desired = typeString(this.desiredType);
String jwkType = typeString(jwk);
String msg = "Value must be " + articleFor(desired) + " " + desired + ", not " +
articleFor(jwkType) + " " + jwkType + ".";
return new IllegalArgumentException(msg);
}
@Override
public T applyFrom(Object o) {
Assert.notNull(o, "JWK cannot be null.");
if (desiredType.isInstance(o)) {
return desiredType.cast(o);
} else if (o instanceof Jwk<?>) {
throw unexpectedIAE((Jwk<?>) o);
}
if (!(o instanceof Map)) {
String msg = "JWK must be a Map<String,?> (JSON Object). Type found: " + o.getClass().getName() + ".";
throw new IllegalArgumentException(msg);
}
final Map<?, ?> map = Collections.immutable((Map<?, ?>) o);
Parameter<String> param = AbstractJwk.KTY;
// mandatory for all JWKs: https://datatracker.ietf.org/doc/html/rfc7517#section-4.1
// no need for builder param type conversion overhead if this isn't present:
if (Collections.isEmpty(map) || !map.containsKey(param.getId())) {
String msg = "JWK is missing required " + param + " parameter.";
throw new MalformedKeyException(msg);
}
Object val = map.get(param.getId());View on GitHub (pinned to fb71496164)
Solutions
- Check the message: the 'not a ...' part names the actual JWK type and the 'must be' part names the required type; convert your data to the required kind (e.g. derive the public JWK from the private Jwk via its `toPublicJwk()`-style accessor or `Keys`/builder utilities) before passing it.
- If you have the JWK as a Map, remove the private-only parameters ("d", "p", "q", "dp", "dq", "qi") so it parses as a public JWK.
- If you actually need to accept private/secret JWKs, use a converter/parser typed for Jwk or the appropriate private Jwk interface (e.g. JwkConverter.ANY) instead of the PublicJwk-typed one.
- Verify you are not accidentally passing a Jwk object where a Map (raw JSON JWK) or Key object was expected; if the value is already the right Jwk, pass the underlying Key instead if the API accepts keys.
Example fix
// before (private JWK passed where public is required) EcPrivateJwk priv = ...; parser.parse(priv); // Value must be an EC Public JWK, not an EC Private JWK. // after EcPublicJwk pub = priv.toPublicJwk(); parser.parse(pub);
Defensive patterns
Strategy: type-guard
Validate before calling
if (jwk instanceof PublicJwk) {
parser.parse(jwk); // safe
} else if (jwk instanceof PrivateJwk) {
parser.parse(((EcPrivateJwk) jwk).toPublicJwk());
} Type guard
boolean isUsableForPublicContext(Jwk<?> jwk) {
return jwk instanceof PublicJwk;
} Try / catch
try {
T jwk = converter.applyFrom(value);
} catch (IllegalArgumentException e) {
// message states required vs actual JWK type; convert or reject
throw new IllegalArgumentException("Unsupported JWK kind for this context: " + e.getMessage(), e);
} Prevention
- Keep private and public JWKs in separate variables/types so the compiler flags misuse.
- Convert private JWKs to public form at the boundary where you publish or embed them.
- Never embed SecretJwk or private JWKs in JWT headers or shared JWKS documents.
- Prefer parsing from raw JSON Maps only when you control the source and know which key kind it contains.
When it happens
Trigger: Calling applyFrom (directly or via parsing APIs like JwkSet parsing, or a Parser<PublicJwk<?>>) and passing an already-constructed Jwk instance whose concrete type (SecretJwk, RsaPrivateJwk, EcPrivateJwk, OctetPrivateJwk, etc.) does not match the converter's desiredType (e.g. PUBLIC_JWK converter receiving a private or secret Jwk). Hit at JwkConverter.java:127-131 where `o instanceof Jwk<?>` but `!desiredType.isInstance(o)`.
Common situations: Passing a private key JWK where the API only accepts public keys (e.g. embedding a JWK in a JWT header or JWE recipient, which requires public keys); confusing a SecretJwk (symmetric key) with an RSA/EC public key; using a converter instantiated for PublicJwk on data from a JWKS that contains private keys; refactors that changed the JWK kind (private -> public or vice versa) without updating the parser/decoder type.
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
- Unable to create ${type.getSimpleName()} from JWK ${ctx}: ${
- Unrelated key operations are not allowed. KeyOperation [${in
- MAC ${keyType} keys must be SecretKey instances. Specified
- Either a Key instance or a kty value is required to create a
- Unable to derive ECPublicKey from ECPrivateKey: ${e.getMessa
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/435e7379ccf71d67.
Report an issue: GitHub.