apereo/cas · error · IllegalArgumentException

JWT string claim is missing or invalid

Error message

JWT string claim is missing or invalid

What it means

requiredStringClaim extracts a named JWT claim and requires it to be a non-blank String. Called by issuer, credentialType, clientId, and validateKeyBindingJwt, it throws IllegalArgumentException when the claim is missing, not a string, or blank.

Solutions

  1. Add the required claim with a non-blank string value before signing the JWT
  2. Check which claim failed (iss/clientId/credentialType) via the call site and inspect the token payload in a JWT decoder
  3. Fix client configuration that leaves the claim empty (e.g. unconfigured client_id)

Example fix

// before
claims.setStringClaim("client_id", ""); // blank
// after
claims.setStringClaim("client_id", "my-client"); // non-blank string
Defensive patterns

Strategy: validation

Validate before calling

boolean hasClaim(Map<String,Object> claims, String name) {
  Object v = claims.get(name);
  return v instanceof String s && !s.isBlank();
}

Prevention

When it happens

Trigger: Key-binding JWT or credential-response JWT lacks a required claim (iss, credential type, client_id, nonce, etc.), or the claim value is null, a non-string type, or an empty/whitespace string.

Common situations: Clients omitting required claims when building the key-binding JWT; empty-string claims from misconfigured clients; claim name typos producing null lookups.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/7dbd0790626aedce. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/vc/presentation/OidcVerifiableCredentialPresentationResponseEndpointController.java:470

        }
    }

    private static List<String> readAudience(final Map<String, Object> claims) {
        val audience = claims.get("aud");
        if (audience instanceof final String value) {
            return List.of(value);
        }
        if (audience instanceof final List<?> values
            && values.stream().allMatch(String.class::isInstance)) {
            return values.stream().map(String.class::cast).toList();
        }
        throw new IllegalArgumentException("JWT audience is invalid");
    }

    private static String requiredStringClaim(final Map<String, Object> claims, final String name) {
        val value = claims.get(name);
        if (!(value instanceof final String stringValue) || stringValue.isBlank()) {
            throw new IllegalArgumentException("JWT string claim is missing or invalid");
        }
        return stringValue;
    }

    private static boolean constantTimeEquals(final String left, final String right) {
        return MessageDigest.isEqual(left.getBytes(StandardCharsets.UTF_8), right.getBytes(StandardCharsets.UTF_8));
    }

    private static ResponseEntity<Map<String, Object>> buildResponse(final HttpStatus status,
                                                                     final Map<String, Object> body) {
        return ResponseEntity.status(status)
            .cacheControl(CacheControl.noStore())
            .header(HttpHeaders.PRAGMA, "no-cache")
            .body(body);
    }

    private static void require(final boolean condition, final String message) {
        if (!condition) {

View on GitHub (pinned to e7288fc434)