quarkusio/quarkus · error · IllegalArgumentException
CDI event payload cannot contain unresolved type variable; f
Error message
CDI event payload cannot contain unresolved type variable; found type:
What it means
When firing a CDI event, ArC must compute the concrete event type to match observers. If, after resolving against the injection point type hierarchy and the runtime class hierarchy, the type still contains an unresolved type variable, the container throws IllegalArgumentException per the CDI specification. This means observers could never reliably receive the event.
Source
Thrown at independent-projects/arc/runtime/src/main/java/io/quarkus/arc/impl/EventImpl.java:248
}
if (Types.containsTypeVariable(resolvedType)) {
/*
* Examining the hierarchy of the specified type did not help. This may still be one of the cases when combining the
* event type and the specified
* type reveals the actual values for type variables. Let's try that.
*/
Type canonicalEventType = Types.getCanonicalType(runtimeType);
TypeResolver objectTypeResolver = new EventObjectTypeResolverBuilder(
injectionPointTypeHierarchy.getResolver().getResolvedTypeVariables(),
new HierarchyDiscovery(canonicalEventType).getResolver().getResolvedTypeVariables()).build();
resolvedType = objectTypeResolver.resolveType(canonicalEventType);
}
/*
* If the runtime type of the event object still contains an unresolved type variable,
* the container must throw an IllegalArgumentException.
*/
if (Types.containsTypeVariable(resolvedType)) {
throw new IllegalArgumentException(
"CDI event payload cannot contain unresolved type variable; found type: " + resolvedType);
}
return resolvedType;
}
private void handleExceptions(ObserverExceptionHandler handler) {
List<Throwable> handledExceptions = handler.getHandledExceptions();
if (!handledExceptions.isEmpty()) {
CompletionException exception = null;
if (handledExceptions.size() == 1) {
exception = new CompletionException(handledExceptions.get(0));
} else {
exception = new CompletionException(null);
}
// always add exceptions into suppressed
for (Throwable handledException : handledExceptions) {
exception.addSuppressed(handledException);
}View on GitHub (pinned to e1c734241f)
Solutions
- Fire a payload whose runtime type is fully concrete (no open type variables)
- Create a concrete subclass of the generic payload (e.g. new StringResult() extends Result<String>) so the type variables are bound
- Pass a fully resolved type when obtaining the Event so the resolver has type information to work with
- Refactor the payload to be non-generic or use a dedicated concrete event record per payload type
Example fix
// before
class Publisher<T> {
@Inject Event<Result<T>> event;
void publish(T value) { event.fire(new Result<>(value)); } // runtime type Result<T> unresolved
}
// after
class StringPublisher {
@Inject Event<Result<String>> event;
void publish(String value) { event.fire(new Result<>(value)); }
} Defensive patterns
Strategy: validation
Validate before calling
static void assertConcretePayload(Object event) {
if (io.quarkus.arc.impl.Types.containsTypeVariable(event.getClass())) {
throw new IllegalArgumentException("Event payload has unresolved type variables: " + event.getClass());
}
}
// call before event.fire(x) Type guard
static boolean isFireable(Object payload) {
return !io.quarkus.arc.impl.Types.containsTypeVariable(payload.getClass());
} Try / catch
try {
event.fire(payload);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("CDI event payload cannot contain unresolved type variable")) {
log.errorf("Payload %s is generic; use a concrete subclass", payload.getClass());
}
throw e;
} Prevention
- Prefer concrete event records/classes (e.g. OrderCreated) over generic wrappers (Result<T>) as payloads
- If payloads must be generic, create named concrete subclasses to fix the type parameters
- Avoid firing events from generic base classes where T is unbound at runtime
When it happens
Trigger: Calling event.fire(x) or fireAsync() where the runtime class of the payload is a parameterized type with unbound variables, e.g. firing an instance of a generic class ListWrapper<T> from generic code where T was never bound.
Common situations: Firing generic payloads like Result<T> or Wrapper<T> from generic service methods; firing events inside generic repository base classes; payload classes that extend generic superclasses without fixing the type parameters.
Related errors
- Event injection point can never be raw type - please specify
- Event#select(TypeLiteral, Annotation...) cannot be used with
- InterceptionProxy parameter must be a parameterized type: ${
- Producer field type is a parameterized type with a type vari
- Declaring class of a managed bean is generic, its scope must
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/470f31641974aca8.
Report an issue: GitHub.