quarkusio/quarkus · error · RuntimeException

Multiple @Inject constructors on ${theClass}

Error message

Multiple @Inject constructors on ${theClass}

What it means

When recording a class instantiation, the recorder uses the @Inject-annotated constructor if one exists. CDI rules allow at most one @Inject constructor; finding two makes constructor selection ambiguous, so Quarkus fails the build. This almost always indicates a violation of CDI semantics in the class being recorded.

Source

Thrown at core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java:1788

    final class NewRecorder extends DeferredArrayStoreParameter {
        final Class<?> theClass;
        final Constructor<?> injectCtor;
        final List<DeferredParameter> deferredParameters = new ArrayList<>();

        NewRecorder(Class<?> theClass) {
            super(theClass.getName());
            this.theClass = theClass;
            Constructor<?> injectCtor = null;
            Constructor<?>[] ctors = theClass.getDeclaredConstructors();
            if (ctors.length == 1) {
                injectCtor = ctors[0];
            } else {
                for (var i : ctors) {
                    if (i.isAnnotationPresent(Inject.class)) {
                        if (injectCtor == null) {
                            injectCtor = i;
                        } else {
                            throw new RuntimeException("Multiple @Inject constructors on " + theClass);
                        }
                    }
                }
                if (injectCtor == null) {
                    throw new RuntimeException(
                            "Could not determine constructor for " + theClass + " add @Inject to a constructor");
                }
            }
            this.injectCtor = injectCtor;
        }

        void preWrite(Map<Object, DeferredParameter> parameterMap) {
            if (injectCtor != null) {
                try {
                    java.lang.reflect.Type[] parameterTypes = injectCtor.getGenericParameterTypes();
                    Annotation[][] parameterAnnotations = injectCtor.getParameterAnnotations();
                    for (int i = 0; i < parameterTypes.length; i++) {
                        java.lang.reflect.Type param = parameterTypes[i];

View on GitHub (pinned to e1c734241f)

Solutions

  1. Keep @Inject on exactly one constructor and remove it from the others
  2. If multiple construction paths are needed, keep one @Inject constructor and provide other constructors without the annotation
  3. Annotate only the canonical constructor (e.g. the record's canonical constructor) used for injection

Example fix

// before
@Inject public Foo(A a) {...}
@Inject public Foo(A a, B b) {...}
// after
@Inject public Foo(A a, B b) {...}
public Foo(A a) { this(a, null); }
Defensive patterns

Strategy: validation

Validate before calling

long injectCtors = Arrays.stream(clazz.getConstructors())
    .filter(c -> c.isAnnotationPresent(Inject.class)).count();
if (injectCtors > 1) {
    throw new IllegalStateException(clazz + " has " + injectCtors + " @Inject constructors");
}

Type guard

boolean singleInjectCtor(Class<?> c) {
    return Arrays.stream(c.getConstructors())
        .filter(k -> k.isAnnotationPresent(Inject.class)).count() <= 1;
}

Try / catch

try {
    recorder.record(value);
} catch (RuntimeException e) {
    if (String.valueOf(e.getMessage()).startsWith("Multiple @Inject constructors")) {
        throw new IllegalStateException("Keep @Inject on exactly one constructor of " + value.getClass(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Passing to a @Record method a class that declares two constructors annotated with @Inject (invalid CDI); a merge/refactor that duplicated the annotation on an additional constructor.

Common situations: Classes evolved by multiple contributors each annotating their preferred constructor; code-generator output that stamps @Inject on every constructor.

Related errors


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