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

  1. Fire a payload whose runtime type is fully concrete (no open type variables)
  2. Create a concrete subclass of the generic payload (e.g. new StringResult() extends Result<String>) so the type variables are bound
  3. Pass a fully resolved type when obtaining the Event so the resolver has type information to work with
  4. 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

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


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