jwtk/jjwt · error · java.lang.IllegalArgumentException
JWK map keys must be Strings. Encountered key '${key}' of ty
Error message
JWK map keys must be Strings. Encountered key '${key}' of type ${type}. What it means
JWK member names must be JSON strings. During map-to-JWK conversion, applyFrom iterates map entries and throws IllegalArgumentException (via the explicit check) if any key is not a String, and asserts no key is null.
Source
Thrown at impl/src/main/java/io/jsonwebtoken/impl/security/JwkConverter.java:167
}
if (!(val instanceof String)) {
String msg = "JWK " + param + " value must be a String. Type found: " + val.getClass().getName();
throw new MalformedKeyException(msg);
}
String kty = (String) val;
if (!Strings.hasText(kty)) {
String msg = "JWK " + param + " value cannot be empty.";
throw new MalformedKeyException(msg);
}
DynamicJwkBuilder<?, ?> builder = this.supplier.get();
for (Map.Entry<?, ?> entry : map.entrySet()) {
Object key = entry.getKey();
Assert.notNull(key, "JWK map key cannot be null.");
if (!(key instanceof String)) {
String msg = "JWK map keys must be Strings. Encountered key '" + key + "' of type " +
key.getClass().getName() + ".";
throw new IllegalArgumentException(msg);
}
String skey = (String) key;
builder.add(skey, entry.getValue());
}
Jwk<?> jwk = builder.build();
if (desiredType.isInstance(jwk)) {
return desiredType.cast(jwk);
}
throw unexpectedIAE(jwk);
}
}
View on GitHub (pinned to fb71496164)
Solutions
- Convert all map keys to Strings before building the JWK (e.g. enum.name(), String.valueOf(key))
- Fix upstream deserialization so JSON object keys stay Strings
- Use Properties.stringPropertyNames() or equivalent when copying from Properties
Example fix
// before
Map<Object,Object> m = props; // contains Integer keys
builder.build(m);
// after
Map<String,Object> m = props.entrySet().stream()
.collect(Collectors.toMap(e -> String.valueOf(e.getKey()), Map.Entry::getValue));
builder.build(m); Defensive patterns
Strategy: type-guard
Validate before calling
for (Object k : jwkMap.keySet()) { if (!(k instanceof String)) throw new IllegalArgumentException("All JWK keys must be Strings"); } Type guard
boolean allStringKeys(Map<?,?> m) { return m != null && m.keySet().stream().allMatch(k -> k instanceof String); } Try / catch
try { jwk = converter.applyFrom(map); } catch (IllegalArgumentException e) { log.error("Bad JWK map keys: {}", e.getMessage()); } Prevention
- Always build JWK maps as Map<String,Object>
- Convert Properties/YAML keys with String.valueOf before use
When it happens
Trigger: Passing a Map with non-String keys (e.g. Integer, enum instances) to JWK building/parsing APIs.
Common situations: Programmatically built maps using enum or numeric keys; converting from formats that allow non-string keys (e.g. YAML, Java Properties).
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
- JWK kty value must be a String. Type found: ${type}
- ${message}Object of class [${objClassName}] must be an insta
- ${message}${subType} is not assignable to ${superType}
- JWT Claim '<expectedClaimName>' was expected to be a Date, b
- JWK must be a Map<String,?> (JSON Object). Type found: ${typ
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/9ed68a5246e31678.
Report an issue: GitHub.