quarkusio/quarkus · error · UnsupportedOperationException
Unsupported wildcard type: ${wildcard}
Error message
Unsupported wildcard type: ${wildcard} What it means
When recording a java.lang.reflect.WildcardType parameter, Quarkus can emit lower-bounded wildcards (? super X); if the wildcard has no lower bound in the branch being handled (e.g. only an upper bound '? extends X' is unsupported here), it throws UnsupportedOperationException. This is a limitation of the recorder's type-parameter recording support.
Source
Thrown at core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java:843
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.invokeStaticMethod(ofMethod(WildcardTypeImpl.class, "withUpperBound",
WildcardType.class, java.lang.reflect.Type.class), context.loadDeferred(res));
}
};
} else if (lowerBound.length == 1) {
// lower bound
DeferredParameter res = loadObjectInstance(lowerBound[0], existing,
java.lang.reflect.Type.class, relaxedValidation);
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.invokeStaticMethod(ofMethod(WildcardTypeImpl.class, "withLowerBound",
WildcardType.class, java.lang.reflect.Type.class), context.loadDeferred(res));
}
};
} else {
throw new UnsupportedOperationException("Unsupported wildcard type: " + wildcard);
}
} else if (expectedType == boolean.class || expectedType == Boolean.class || param instanceof Boolean) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((boolean) param);
}
};
} else if (expectedType == int.class || expectedType == Integer.class || param instanceof Integer) {
return new DeferredParameter() {
@Override
ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {
return method.load((int) param);
}
};
} else if (expectedType == short.class || expectedType == Short.class || param instanceof Short) {
return new DeferredParameter() {
@OverrideView on GitHub (pinned to e1c734241f)
Solutions
- Avoid passing raw WildcardType objects to recorders; resolve the wildcard to its concrete upper bound (e.g. via quarkus-extension's ReflectUtil or GAST types) before recording
- Use parameterized Class/TypeToken-style concrete types instead of wildcards
- Check Quarkus version — support for wildcard bounds has changed; upgrade to a version handling your wildcard shape
- If you control the type source, replace '? extends X' declarations with the concrete type X
- File an issue if your wildcard type should be supported (lower-bound wildcards are handled)
Example fix
// before Type t = field.getGenericType(); // contains '? extends Foo' recorder.setType(t); // after Type t = resolveWildcardToUpperBound(field.getGenericType()); // yields Foo.class recorder.setType(t);
Defensive patterns
Strategy: type-guard
Validate before calling
static boolean isRecorderSafeType(java.lang.reflect.Type t) {
if (t instanceof java.lang.reflect.WildcardType w) {
return w.getLowerBounds().length > 0; // only lower-bounded wildcards are supported
}
return true;
} Type guard
static boolean isWildcardWithLowerBound(java.lang.reflect.Type t) {
return t instanceof java.lang.reflect.WildcardType w && w.getLowerBounds().length > 0;
} Try / catch
try {
recorder.setType(genericType);
} catch (UnsupportedOperationException e) {
if (e.getMessage().startsWith("Unsupported wildcard")) {
recorder.setType(resolveToUpperBound(genericType));
} else { throw e; }
} Prevention
- Resolve wildcards to concrete upper-bound types before recording
- Avoid '? extends' bounds in types that cross the recorder boundary
- Pin/upgrade Quarkus versions deliberately — wildcard support evolves
When it happens
Trigger: Passing a reflectively-obtained Type that is a WildcardType with an upper bound only (e.g. '? extends Foo'), or an unbounded '?' not covered by the handled cases, into a recorder method parameter of type Type.
Common situations: Extension code passing generic reflection metadata (from ParameterizedType fields or generated config mappings) into recorders; using types captured from user classes with '? extends' bounds.
Related errors
- Failed to record call to method ${call.method}
- All parameters have already been loaded, it is too late to c
- String too large to record: ${param}
- Cannot call getValue() at deployment time
- Invalid type:
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/0eaff680aabf9f4c.
Report an issue: GitHub.