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

JWK Set keys collection cannot be empty.

Error message

JWK Set keys collection cannot be empty.

What it means

Thrown by JwkSetConverter.applyFrom when the JWK Set's keys member is an empty collection. A JWK Set must contain at least one key to be usable, so an empty keys array is rejected as a malformed JWK Set (MalformedKeySetException).

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/JwkSetConverter.java:102

        // 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());
        }

        Set<Jwk<?>> jwks = new LinkedHashSet<>(size);
        int i = 0; // keep track of which element fails (if any)
        for (Object candidate : ((Collection<?>) val)) {

View on GitHub (pinned to fb71496164)

Solutions

  1. Ensure the JWKS source contains at least one key
  2. Check the issuer/JWKS endpoint for provisioning or outage issues
  3. Treat empty keys as retryable/re-fetchable upstream (cache refresh) and surface a clear error to users

Example fix

// before
{"keys":[]}
// after
{"keys":[{"kty":"RSA","n":"...","e":"AQAB","kid":"key-1","alg":"RS256"}]}
Defensive patterns

Strategy: validation

Validate before calling

Collection<?> keys = (Collection<?>) m.get("keys"); if (keys == null || keys.isEmpty()) { throw new IllegalArgumentException("JWKS must contain at least one key"); }

Type guard

boolean hasAtLeastOneKey(Map<?,?> m) { Object k = m == null ? null : m.get("keys"); return k instanceof Collection<?> c && !c.isEmpty(); }

Try / catch

try { jwkSet = Jwks.setParser().build().parse(json); } catch (MalformedKeySetException e) { scheduleJwksRefresh(); log.warn("Empty JWKS from upstream: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Parsing {"keys":[]} or building a JWK Set with an empty collection.

Common situations: IdP JWKS endpoint returning an empty array during outages or before keys are provisioned; key rotation removing all keys; misconfigured issuer URL returning a valid-but-empty document.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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