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
- Keep @Inject on exactly one constructor and remove it from the others
- If multiple construction paths are needed, keep one @Inject constructor and provide other constructors without the annotation
- 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
- Follow CDI rule: at most one @Inject constructor per class
- Audit generated/refactored code for duplicated @Inject annotations
- Prefer a single canonical constructor and delegate via this(...)
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
- @CachedResults class must be an interface or declare a no-ar
- A context class must either declare a no-args constructor or
- Unable to serialize ${param} as the wrong number of paramete
- Unable to determine the recordable constructor to use for ${
- Could not find parameters for constructor ${nonDefaultConstr
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/de23b81adbb6348e.
Report an issue: GitHub.