quarkusio/quarkus · error · io.quarkus.oidc.runtime.OIDCException

Claim value at path '%s' is not a string

Error message

Claim value at path '%s' is not a string

What it means

Quarkus OIDC's OidcUtils.findStringClaimValue throws OIDCException when a token claim located at the given path exists but its value is not a JSON string. Code expecting a string claim (e.g. 'sub', 'preferred_username') receives a number, boolean, array, or object instead.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcUtils.java:337

            return Arrays.asList(claimValue.toString().split(sep));
        } else {
            return Collections.emptyList();
        }
    }

    private static String[] splitClaimPath(String claimPath) {
        return claimPath.indexOf('/') > 0 ? CLAIM_PATH_PATTERN.split(claimPath) : new String[] { claimPath };
    }

    static String findStringClaimValue(String claimPath, JsonObject json) {
        Object value = findClaimValue(claimPath, json, splitClaimPath(claimPath), 0);
        if (value == null) {
            return null;
        }
        if (value instanceof String) {
            return (String) value;
        }
        throw new OIDCException("Claim value at path '" + claimPath + "' is not a string");
    }

    private static Object findClaimValue(String claimPath, JsonObject json, String[] pathArray, int step) {
        Object claimValue = json.getValue(pathArray[step].replace("\"", ""));
        if (claimValue == null) {
            LOG.debugf("No claim exists at the path '%s' at the path segment '%s'", claimPath, pathArray[step]);
        } else if (step + 1 < pathArray.length) {
            if (claimValue instanceof JsonObject) {
                int nextStep = step + 1;
                return findClaimValue(claimPath, (JsonObject) claimValue, pathArray, nextStep);
            } else {
                LOG.debugf("Claim value at the path '%s' is not a json object", claimPath);
            }
        }

        return claimValue;
    }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Fix the claim path configuration so it points to an actual string claim in the token
  2. Transform the token at the identity provider so the claim is emitted as a string (e.g. 'sub' as string)
  3. Use a claim-value accessor that handles non-string JSON types instead of findStringClaimValue

Example fix

// before: 'sub': 12345 (number) -> OIDCException
// after: configure IdP or mapping so 'sub': "12345" (string)
Defensive patterns

Strategy: type-guard

Validate before calling

Object claim = jwtJson.getJson("claimPath");
if (!(claim instanceof String)) throw new IllegalArgumentException("Claim must be a string: " + claim);

Type guard

boolean isStringClaim(Object v) { return v instanceof String; }

Try / catch

try { value = findStringClaimValue(token, claimPath); } catch (OIDCException e) { log.warn("Claim {} not a string, falling back", claimPath); value = String.valueOf(findClaimValue(...)); }

Prevention

When it happens

Trigger: Calling findStringClaimValue (or configured claim paths like name/subject claim paths) on a token where the claim at claimPath holds a non-string JSON value (e.g. an integer 'sub', a JSON object), so the instanceof String check fails and OIDCException is thrown.

Common situations: IdP-issued tokens with numeric 'sub' claims (some providers use integers); custom claim mappings pointing at nested objects or arrays; misconfigured quarkus.oidc token.claim-path settings pointing at the wrong claim.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/c1bd77db625b246c. Report an issue: GitHub.