quarkusio/quarkus · error · org.jose4j.jwt.MalformedClaimException
expected claim %s must be a list of strings or a string
Error message
expected claim %s must be a list of strings or a string
What it means
Thrown by OidcProvider's CustomClaimsValidator when a required claim configured in quarkus.oidc.token.required-claims is present in the token but is neither a string nor a list of strings (e.g. a number, boolean, or object). Only string and string-list claims can be validated against the configured expected values, so MalformedClaimException is thrown.
Source
Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java:757
if (requiredClaimValues.size() == 1) {
String actualClaimValue = claims.getStringClaimValue(requiredClaimName);
String requiredClaimValue = requiredClaimValues.iterator().next();
if (!requiredClaimValue.equals(actualClaimValue)) {
return "claim " + requiredClaimName + " does not match expected value of " + requiredClaimValues;
}
} else {
throw new MalformedClaimException("expected claim " + requiredClaimName + " must be a list of strings");
}
} else {
if (claims.isClaimValueStringList(requiredClaimName)) {
List<String> actualClaimValues = claims.getStringListClaimValue(requiredClaimName);
for (String requiredClaimValue : requiredClaimValues) {
if (!actualClaimValues.contains(requiredClaimValue)) {
return "claim " + requiredClaimName + " does not match expected value of " + requiredClaimValues;
}
}
} else {
throw new MalformedClaimException(
"expected claim " + requiredClaimName + " must be a list of strings or a string");
}
}
return null;
}
}
private static Map<String, Object> tokenMap(String token, TokenType tokenType) {
return Map.of(tokenType == TokenType.ID_TOKEN ? OidcConstants.ID_TOKEN_VALUE : OidcConstants.ACCESS_TOKEN_VALUE,
token);
}
private static final class CatchingErrorCodeValidator extends ErrorCodeValidatorAdapter {
private AuthenticationFailedException authenticationFailure;
private CatchingErrorCodeValidator(Validator validator) {
super(validator);View on GitHub (pinned to e1c734241f)
Solutions
- Reference only string-valued claims in required-claims (decode the token to check the JSON type)
- If the claim is a number/boolean, implement a custom Validator bean instead of using required-claims config
- Ask the issuer to emit the claim as a string (e.g. "loa": "2" instead of 2)
- Verify the claim name isn't accidentally colliding with a different claim of non-string type
Example fix
// token: "loa": 2 (number)
// before: quarkus.oidc.token.required-claims.loa=2 -> throws
// after: custom Validator bean:
public class LoaValidator implements Validator {
public Error validate(JwtContext ctx) {
return ctx.getJwtClaims().getClaimValue("loa", Integer.class) >= 2 ? null : Error.authFailure("loa too low");
}
} Defensive patterns
Strategy: type-guard
Validate before calling
// ensure the claim value type matches what required-claims supports
Object v = decodedClaims.get(claimName);
if (v != null && !(v instanceof String) && !(v instanceof List)) {
// non-string claim (number/boolean/object): use a custom Validator instead
} Type guard
boolean isValidatableClaim(Object v) {
return v instanceof String
|| (v instanceof List<?> l && l.stream().allMatch(String.class::isInstance));
} Prevention
- Only reference string/string-list claims in required-claims
- Check claim types by decoding a production token, not by claim name alone
- Convert numeric/boolean claims to strings at the issuer or validate them in code
When it happens
Trigger: quarkus.oidc.token.required-claims.<name> configured for a claim that the token carries as a non-string JSON type — for example "loa": 2 (number) or "roles": {"admin": true} (object).
Common situations: Identity providers emitting numeric authentication levels or structured claims that developers reference in required-claims; copy-pasting claim names from decoded JSON without noticing the value type differs from what's configured.
Related errors
- expected claim %s must be a list of strings
- Unrecognized dependency flag '<trimmed>'. Supported flags: O
- Parameter 'mode' was set to '<mode>' while expected one of '
- Parameter 'mode' was set to '<mode>' while expected one of '
- Parameter 'mode' was set to '<mode>' while expected one of '
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/b504e4765aede88e.
Report an issue: GitHub.