jwtk/jjwt · error · io.jsonwebtoken.security.MalformedKeySetException
JWK Set keys value must be a Collection (JSON Array). Type…
Error message
JWK Set keys value must be a Collection (JSON Array). Type found: ${type} What it means
Thrown by JwkSetConverter.applyFrom when the keys member of a JWK Set is present and non-null but is not a JSON Array (Collection). RFC 7517 §5 requires keys to be an array of JWK objects, so any other JSON type marks the set malformed and conversion fails with MalformedKeySetException.
Solutions
- Wrap the single JWK in a JSON array: "keys":[{...}]
- Fix the serializer/deserializer so keys is always an array
- Validate the JWKS shape before passing it to jjwt
Example fix
// before
{"keys":{"kty":"RSA","n":"...","e":"AQAB"}}
// after
{"keys":[{"kty":"RSA","n":"...","e":"AQAB"}]} Defensive patterns
Strategy: type-guard
Validate before calling
if (!(m.get("keys") instanceof Collection)) { throw new IllegalArgumentException("'keys' must be a JSON array"); } Type guard
boolean keysIsArray(Map<?,?> m) { return m != null && m.get("keys") instanceof Collection<?>; } Try / catch
try { jwkSet = jwkSetConverter.applyFrom(map); } catch (MalformedKeySetException e) { log.error("keys must be an array: {}", e.getMessage()); } Prevention
- Always serialize single keys as a one-element array
- Round-trip JWKS documents through your serializer in tests
When it happens
Trigger: JWKS input where "keys" is a single JWK object (Map) instead of an array, or a JSON string.
Common situations: Hand-written JWKS with a single key written as an object rather than a one-element array; custom serializers flattening single-element arrays.
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
- Value must be a Map<String,?> (JSON Object). Type found
- JWK kty value must be a String. Type found
- JWK map keys must be Strings. Encountered key
- JWK Set keys collection cannot be empty.
- JWK Set keys value cannot be null.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/bbb453b8054dc3d2.
Report an issue: GitHub.
Appendix: source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/JwkSetConverter.java:97
throw new IllegalArgumentException(msg);
}
final Map<?, ?> m = Collections.immutable((Map<?, ?>) o);
// mandatory for all JWK Sets: https://datatracker.ietf.org/doc/html/rfc7517#section-5
// no need for builder parameter type conversion overhead if this isn't present:
if (Collections.isEmpty(m) || !m.containsKey(PARAM.getId())) {
String msg = "Missing required " + PARAM + " parameter.";
throw new MalformedKeySetException(msg);
}
Object val = m.get(PARAM.getId());
if (val == null) {
String msg = "JWK Set " + PARAM + " value cannot be null.";
throw new MalformedKeySetException(msg);
}
if (!(val instanceof Collection)) {
String msg = "JWK Set " + PARAM + " value must be a Collection (JSON Array). Type found: " +
val.getClass().getName();
throw new MalformedKeySetException(msg);
}
int size = Collections.size((Collection<?>) val);
if (size == 0) {
String msg = "JWK Set " + PARAM + " collection cannot be empty.";
throw new MalformedKeySetException(msg);
}
// Copy values so we don't mutate the original input
Map<String, Object> src = new LinkedHashMap<>(Collections.size((Map<?, ?>) o));
for (Map.Entry<?, ?> entry : ((Map<?, ?>) o).entrySet()) {
Object key = Assert.notNull(entry.getKey(), "JWK Set map key cannot be null.");
if (!(key instanceof String)) {
String msg = "JWK Set map keys must be Strings. Encountered key '" + key + "' of type " +
key.getClass().getName();
throw new IllegalArgumentException(msg);
}
String skey = (String) key;
src.put(skey, entry.getValue());View on GitHub (pinned to fb71496164)