quarkusio/quarkus · error · UnsupportedOperationException

Cannot construct empty build items

Error message

Cannot construct empty build items

What it means

EmptyBuildItem represents a build item used only as a signal, consumed/produced via @Consume/@Produce without an actual instance. Its (protected) constructor unconditionally throws UnsupportedOperationException, so any attempt to directly instantiate an EmptyBuildItem subclass fails with "Cannot construct empty build items". It is a deliberate API guard, not a runtime fault.

Source

Thrown at core/builder/src/main/java/io/quarkus/builder/item/EmptyBuildItem.java:12

package io.quarkus.builder.item;

/**
 * An empty build item. Empty build items carry no data and may be used, for example, for ordering and for
 * running steps which don't otherwise produce anything.
 *
 * Empty build items cannot be instantiated, you must use <code>@Produce(MyEmptyBuildItem.class)</code> or
 * <code>@Consume(MyEmptyBuildItem.class)</code> instead of the standard ways to consume or produce build items.
 */
public abstract class EmptyBuildItem extends BuildItem {
    protected EmptyBuildItem() {
        throw new UnsupportedOperationException("Cannot construct empty build items");
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Do not instantiate it: reference the class literal in annotations, e.g. @BuildStep @Produce(MyEmptyBuildItem.class) ... or declare the item type as a build-step parameter for consumption.
  2. If the step must actually produce a value-bearing item, switch the class to extend BuildItem (non-empty) and return a real instance.
  3. Remove any `new MyEmptyBuildItem()` calls, including in tests and reflection-based code.
  4. For pure ordering, produce/consume via @Produce/@Consume annotations or MultiBuildItem-free signal types exactly as the class Javadoc describes.

Example fix

// before
@BuildStep
void produceSignal() { new MySignalItem(); }

// after
@BuildStep
@Produce(MySignalItem.class)
void produceSignal() { /* no instance needed */ }
Defensive patterns

Strategy: validation

Validate before calling

// never instantiate; assert usage in tests
static void assertNotInstantiated(Class<? extends EmptyBuildItem> clazz) {
    // only the class literal should be referenced:
    // @Produce(clazz) / @Consume(clazz) / method parameters of type clazz
}

Type guard

static <T extends EmptyBuildItem> T neverCreate(Class<T> clazz) {
    throw new UnsupportedOperationException(
        clazz + " is an EmptyBuildItem: use @Produce/@Consume(" + clazz.getSimpleName() + ".class), not new");
}

Try / catch

try {
    processSignal();
} catch (UnsupportedOperationException e) {
    if (e.getMessage().equals("Cannot construct empty build items")) {
        throw new IllegalStateException("Replace direct construction of " + MySignalItem.class.getSimpleName()
            + " with @Produce/@Consume class references", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling `new MyEmptyBuildItem()` (or via reflection/serialization) on a class extending EmptyBuildItem instead of referencing the class object in @Produce(MyEmptyBuildItem.class) or @Consume(MyEmptyBuildItem.class); a build step method that tries to return a new instance of an empty item.

Common situations: Misunderstanding the empty build item idiom when writing build steps; code generators or tests constructing instances generically; migrating a normal build item to EmptyBuildItem while leaving old `new` call sites in place.

Related errors


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