quarkusio/quarkus · error · IllegalArgumentException
Build item class must be leaf (final) types: %s
Error message
Build item class must be leaf (final) types: %s
What it means
BuildItem's constructor also requires every concrete build item class to be final (a leaf type). If getClass() reports a non-final class, it throws this IllegalArgumentException. Quarkus keys build steps on exact build item classes; subclassing an item would let one production be consumed as multiple item types, so the framework forbids it.
Source
Thrown at core/builder/src/main/java/io/quarkus/builder/item/BuildItem.java:22
/**
* 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
- Mark the build item class final; if you need shared fields, extract them into a plain value object the item holds.
- Stop extending other BuildItems — define a new standalone final build item class instead.
- If you need polymorphic consumption, use a shared payload type or multiple distinct build items rather than a subclass hierarchy.
- Check any dynamically generated or proxied build item classes for the final modifier.
Example fix
// before
public class MyItem extends BuildItem { ... }
// after
public final class MyItem extends BuildItem { ... } Defensive patterns
Strategy: type-guard
Validate before calling
static void assertFinalBuildItems(String packageName) {
// e.g. via ClassGraph/Reflections scan in a unit test
// for each Class<?> c in package: assert Modifier.isFinal(c.getModifiers())
} Type guard
static boolean isLeafBuildItem(Class<?> clazz) {
return BuildItem.class.isAssignableFrom(clazz)
&& Modifier.isFinal(clazz.getModifiers());
} Try / catch
try {
BuildItem item = createMyItem();
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Build item class must be leaf")) {
throw new IllegalStateException("Mark the build item final; do not subclass existing items", e);
}
throw e;
} Prevention
- Always declare BuildItem subclasses with the final modifier
- Do not extend other BuildItems; compose payload objects instead
- Add a unit test scanning the package for non-final BuildItem subclasses
When it happens
Trigger: Declaring `class MyBaseItem extends BuildItem` (non-final) and instantiating it or a subclass; extending an existing concrete build item to add fields instead of composing; instantiating an abstract-ish intermediate class that is not marked final.
Common situations: Inheritance-based reuse of build items when refactoring; attempts to specialize a framework-provided build item by subclassing; copy-pasted item classes missing the final modifier.
Related errors
- A generic type is not allowed here; try creating a subclass
- Cannot construct empty build items
- A build step must be a non-static method: %s
- You have to specify a key. This is the key the user will pre
- You have to specify a description. This is what the user wil
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/1adadb83605c4d6d.
Report an issue: GitHub.