quarkusio/quarkus · error · IllegalArgumentException

A generic type is not allowed here; try creating a subclass

Error message

A generic type is not allowed here; try creating a subclass with concrete type arguments instead: %s

What it means

BuildItem's constructor enforces that every concrete build item is a non-generic, final class. It inspects getClass() at instantiation time and throws this IllegalArgumentException if the runtime class still declares type parameters. The Quarkus build system requires build items to be unique concrete types so they can be keyed in the dependency graph; a generic BuildItem<Multimap<String,String>> cannot be identified unambiguously.

Source

Thrown at core/builder/src/main/java/io/quarkus/builder/item/BuildItem.java:17

package io.quarkus.builder.item;

import java.lang.reflect.Modifier;

/**
 * A build item which can be produced or consumed. Any item
 * which implements {@link AutoCloseable} will be automatically closed when the build
 * is completed, unless it is explicitly marked as a final build result in which case closure is
 * the responsibility of whomever invoked the build execution.
 * <p>
 * Resources should be fine-grained as possible, ideally describing only one aspect of the build process.
 */
public abstract class BuildItem {
    BuildItem() {
        final Class<? extends BuildItem> clazz = getClass();
        if (clazz.getTypeParameters().length != 0) {
            throw new IllegalArgumentException(
                    "A generic type is not allowed here; try creating a subclass with concrete type arguments instead: "
                            + getClass());
        }
        if (!Modifier.isFinal(clazz.getModifiers())) {
            throw new IllegalArgumentException("Build item class must be leaf (final) types: " + getClass());
        }
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Create a concrete final subclass with fixed type arguments, e.g. `public final class StringMapItem extends BuildItem<Map<String,String>>` — wait, instead define `final class MyItem extends BuildItem` with fields, and use that.
  2. Replace the generic type parameter with explicit fields on a final class (e.g. `final class LocaleMapBuildItem extends BuildItem { final Map<String,Locale> map; }`).
  3. If instantiating an anonymous generic subclass, convert it to a named final class so getClass() has no type parameters.
  4. Rebuild after changes — the check runs at construction, so any path constructing the generic class must be migrated.

Example fix

// before
class MyItem<T> extends BuildItem {
    final T payload;
    MyItem(T payload) { this.payload = payload; }
}
new MyItem<Map<String,String>>(map);

// after
public final class MyMapItem extends BuildItem {
    final Map<String,String> payload;
    public MyMapItem(Map<String,String> payload) { this.payload = payload; }
}
new MyMapItem(map);
Defensive patterns

Strategy: type-guard

Validate before calling

static <T extends BuildItem> T validateNonGeneric(Class<? extends T> clazz) {
    if (clazz.getTypeParameters().length != 0) {
        throw new IllegalArgumentException(
            clazz + " is generic; define a concrete final subclass with fixed fields instead");
    }
    return null; // validation-only helper; real instance is constructed after this passes
}

Type guard

static boolean isConcreteBuildItem(Class<?> clazz) {
    return BuildItem.class.isAssignableFrom(clazz)
        && clazz.getTypeParameters().length == 0
        && !clazz.isAnonymousClass();
}

Try / catch

try {
    BuildItem item = new MyMapItem(map);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("A generic type is not allowed here")) {
        throw new IllegalStateException("Refactor " + e.getMessage() + " into a final concrete BuildItem subclass", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Declaring `class MyItem<T> extends BuildItem` and instantiating it (possibly via an anonymous or subclass form) instead of creating a concrete final subclass with fixed type arguments; instantiating a generic item directly with `new MyItem<Something>()`; using an anonymous class of a generic item that retains type parameters.

Common situations: Developers porting code that used generics for payload flexibility; refactoring where a generic base item was previously instantiated directly; anonymous subclassing `new BuildItem<Map<K,V>>(){}` patterns that keep type parameters at runtime.

Related errors


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