quarkusio/quarkus · error · IllegalArgumentException

Type variable %s of %s could not be resolved.

Error message

Type variable %s of %s could not be resolved.

What it means

While resolving the type arguments of a multipart response's parameterized return type, a nested type variable (e.g. the T in Wrapper<T>) could not be found in the ownerIdentifierTypeLookupMap built from the declaration. The generator cannot synthesize a GenericType without a concrete type, so it throws an IllegalArgumentException including the unresolved identifier and the declaration site.

Source

Thrown at extensions/resteasy-reactive/rest-client-jaxrs/deployment/src/main/java/io/quarkus/jaxrs/client/reactive/deployment/JaxrsClientReactiveProcessor.java:1493

    private Type resolveType(Type type,
            Map<String, Type> ownerIdentifierTypeLookupMap, Declaration declaration) {
        if (type.kind() == PARAMETERIZED_TYPE) {

            ParameterizedType parameterizedReturnType = type.asParameterizedType();
            ParameterizedType.Builder methodReturnTypeBuilder = ParameterizedType.builder(type.name());
            for (int i = 0; i < parameterizedReturnType.arguments().size(); i++) {
                Type paramReturnTypeArg = parameterizedReturnType.arguments().get(i);
                Type resolvedType;
                if (paramReturnTypeArg.kind() == TYPE_VARIABLE) {
                    // method returns another subresource, and one of the arguments is a type variable e.g.  Wrapper<T>
                    resolvedType = ownerIdentifierTypeLookupMap.get(paramReturnTypeArg.asTypeVariable().identifier());
                    if (resolvedType == null) {
                        String declarationSite = declaration.toString();
                        if (declaration.kind() == AnnotationTarget.Kind.METHOD) {
                            declarationSite = "method %s in class %s".formatted(declaration,
                                    declaration.asMethod().declaringClass());
                        }
                        throw new IllegalArgumentException(
                                "Type variable %s of %s could not be resolved."
                                        .formatted(paramReturnTypeArg.asTypeVariable().identifier(), declarationSite));
                    }
                } else if (paramReturnTypeArg.kind() == PARAMETERIZED_TYPE) {
                    // parameterized type contains another parameterized type with either a type variable e.g.  Wrapper<List<T>> or without, e.g. Wrapper<List<String>>
                    resolvedType = resolveType(paramReturnTypeArg, ownerIdentifierTypeLookupMap, declaration);
                } else {
                    resolvedType = paramReturnTypeArg;
                }

                methodReturnTypeBuilder.addArgument(resolvedType);
            }

            // rewrite parameterized type to reflect the resolved type variable
            // i.e. Wrapper<String> instead of Wrapper<V>
            return methodReturnTypeBuilder.build();
        } else if (type.kind() == TYPE_VARIABLE) {
            TypeVariable typeVariable = type.asTypeVariable();

View on GitHub (pinned to e1c734241f)

Solutions

  1. Bind the type variable: make the declaring class/interface parameterized with a concrete type (interface MyClient extends GenericClient<MultipartBody>)
  2. Change the multipart method return to a fully concrete type
  3. Verify that all type variables used in the return type appear in the method or declaring class signature

Example fix

// before
class MyClient implements GenericClient<T> { T get(); } // raw T
// after
class MyClient implements GenericClient<MultipartBody> { MultipartBody get(); }
Defensive patterns

Strategy: validation

Validate before calling

static void assertBoundGenerics(Class<?> clientItf) { for (var m : clientItf.getMethods()) { for (var a : ((ParameterizedType) m.getGenericReturnType() instanceof ParameterizedType pt ? pt.getActualTypeArguments() : new Type[0])) { if (a instanceof TypeVariable) throw new IllegalStateException("Unbound type var " + a + " in " + m); } } }

Type guard

boolean allTypeArgsConcrete(Type t) { return !(t instanceof TypeVariable) && (!(t instanceof ParameterizedType pt) || Arrays.stream(pt.getActualTypeArguments()).allMatch(a -> a instanceof Class || (a instanceof ParameterizedType p && allTypeArgsConcrete(p)))); }

Try / catch

try { client.fetch(); } catch (IllegalArgumentException e) { if (e.getMessage().contains("could not be resolved")) { log.error("Bind the type variable named in the message"); } throw e; }

Prevention

When it happens

Trigger: A client method returns e.g. Wrapper<T> or Result<T, E> where a nested type argument is a type variable that no enclosing declaration binds — e.g. the type variable comes from an unparameterized superclass or interface rather than the method itself.

Common situations: Generic base interfaces (Repository<T> with getClient returning T) extended raw; missing explicit type argument in an extends clause; mixing generic helper classes into multipart responses.

Related errors


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