jwtk/jjwt · error · IllegalArgumentException

JWK Set map keys must be Strings. Encountered key '${key}' o

Error message

JWK Set map keys must be Strings. Encountered key '${key}' of type ${key.getClass().getName()}

What it means

Thrown by JwkSetConverter.applyFrom when converting a Map representation of a JWK Set whose top-level keys are not Strings. JWK Set maps must have JSON-string keys (like "keys", "kty"); any non-String key means the input map is not a valid JWK Set representation. The library fails fast with IllegalArgumentException naming the offending key and its Java type.

Source

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

        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)) {
            try {
                Jwk<?> jwk = JWK_CONVERTER.applyFrom(candidate);
                jwks.add(jwk);
            } catch (UnsupportedKeyException e) {
                if (!ignoreUnsupported) {
                    String msg = "JWK Set keys[" + i + "]: " + e.getMessage();
                    throw new UnsupportedKeyException(msg, e);
                }
            } catch (IllegalArgumentException | KeyException e) {
                if (!ignoreUnsupported) {

View on GitHub (pinned to fb71496164)

Solutions

  1. Convert all map keys to Strings before passing the map (e.g. String.valueOf(key)).
  2. Inspect the offending key printed in the message and remove/fix the non-String entry in the source map.
  3. If the data comes from another format (YAML/DB), transform it to a Map<String, Object> first.
  4. If you need non-String identifiers, store them as values inside the JWK entries instead of as map keys.

Example fix

// before
Map<Object, Object> bad = new LinkedHashMap<>();
bad.put(1234, jwkEntry);
JwkSetConverter.getInstance().applyFrom(bad);

// after
Map<String, Object> good = new LinkedHashMap<>();
good.put(String.valueOf(1234), jwkEntry);
JwkSetConverter.getInstance().applyFrom(good);
Defensive patterns

Strategy: validation

Validate before calling

boolean isValidJwkSetMap(Map<?, ?> m) {
    return m != null && m.keySet().stream().allMatch(k -> k instanceof String);
}

Type guard

boolean allStringKeys(Map<?, ?> m) {
    return m.keySet().stream().allMatch(k -> k instanceof String);
}

Try / catch

try {
    converter.applyFrom(map);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("JWK Set map keys must be Strings")) {
        // normalize keys and retry
    } else throw e;
}

Prevention

When it happens

Trigger: Calling JwkSetConverter.applyFrom(o) (directly or via API that parses a JWK Set from a Map) with a LinkedHashMap or other Map containing non-String keys, e.g. an Integer or enum key mixed into the map.

Common situations: Building a JWK Set map programmatically with non-String keys (Integer ids, Key objects, enums); deserializing from a format like YAML or a database row where keys were not normalized to Strings; passing an internal map that was never meant to be a JWK Set.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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