jwtk/jjwt · error · ClassCastException

Cannot cast ${value.getClass().getName()} to ${IDIOMATIC_TYP

Error message

Cannot cast ${value.getClass().getName()} to ${IDIOMATIC_TYPE.getName()}

What it means

DefaultParameter.cast, for non-collection parameters, checks the value is an instance of the declared type (IDIOMATIC_TYPE) and throws a ClassCastException otherwise. This is the plain scalar-path type guard applied to every typed header/claim parameter.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/lang/DefaultParameter.java:88

            if (COLLECTION_TYPE != null) { // parameter represents a collection, ensure it and its elements are the expected type:
                if (!COLLECTION_TYPE.isInstance(value)) {
                    String msg = "Cannot cast " + value.getClass().getName() + " to " +
                            COLLECTION_TYPE.getName() + "<" + IDIOMATIC_TYPE.getName() + ">";
                    throw new ClassCastException(msg);
                }
                Collection<?> c = COLLECTION_TYPE.cast(value);
                if (!c.isEmpty()) {
                    Object element = c.iterator().next();
                    if (!IDIOMATIC_TYPE.isInstance(element)) {
                        String msg = "Cannot cast " + value.getClass().getName() + " to " +
                                COLLECTION_TYPE.getName() + "<" + IDIOMATIC_TYPE.getName() + ">: At least one " +
                                "element is not an instance of " + IDIOMATIC_TYPE.getName();
                        throw new ClassCastException(msg);
                    }
                }
            } else if (!IDIOMATIC_TYPE.isInstance(value)) {
                String msg = "Cannot cast " + value.getClass().getName() + " to " + IDIOMATIC_TYPE.getName();
                throw new ClassCastException(msg);
            }
        }
        return (T) value;
    }

    @Override
    public boolean isSecret() {
        return SECRET;
    }

    @Override
    public int hashCode() {
        return this.ID.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Parameter) {

View on GitHub (pinned to fb71496164)

Solutions

  1. Convert the value to the declared type before assignment (String.valueOf(x), Long.parseLong(s), etc.).
  2. Inspect the token's raw JSON to see the actual type and adjust the value or the parameter registration.
  3. When reading, cast defensively via instanceof checks before using the claim value.

Example fix

// before
Object exp = claims.get("exp"); // Integer
Long l = (Long) exp; // ClassCastException via parameter cast
// after
Object exp = claims.get("exp");
Long l = (exp instanceof Number) ? ((Number) exp).longValue() : Long.parseLong(exp.toString());
Defensive patterns

Strategy: type-guard

Validate before calling

if (value != null && !expectedType.isInstance(value)) {
    value = convert(value, expectedType); // String.valueOf, Long.parseLong, ...
}

Type guard

static <T> T asType(Object v, Class<T> t) {
    return t.isInstance(v) ? t.cast(v) : null;
}

Try / catch

try {
    String s = (String) claims.get("sub");
} catch (ClassCastException e) {
    s = String.valueOf(claims.get("sub"));
}

Prevention

When it happens

Trigger: Setting a claim/header value whose runtime type differs from the registered parameter type, e.g. an Integer where a String is expected, or a String where a Date/Long parameter expects a number; also triggered during JWT deserialization when the JSON type doesn't match the schema.

Common situations: External issuers emitting numeric claims the library declares as strings; copying claims between tokens with different schemas; using raw/untyped builders that bypass generics.

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


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