{"record":{"id":"f7bd25299beaf8a5","repo":"quarkusio/quarkus","slug":"could-not-determine-constructor-for-theclass-ad","errorCode":null,"errorMessage":"Could not determine constructor for ${theClass} add @Inject to a constructor","messagePattern":"Could not determine constructor for (.+?) add @Inject to a constructor","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java","lineNumber":1793,"sourceCode":"        NewRecorder(Class<?> theClass) {\n            super(theClass.getName());\n            this.theClass = theClass;\n            Constructor<?> injectCtor = null;\n            Constructor<?>[] ctors = theClass.getDeclaredConstructors();\n            if (ctors.length == 1) {\n                injectCtor = ctors[0];\n            } else {\n                for (var i : ctors) {\n                    if (i.isAnnotationPresent(Inject.class)) {\n                        if (injectCtor == null) {\n                            injectCtor = i;\n                        } else {\n                            throw new RuntimeException(\"Multiple @Inject constructors on \" + theClass);\n                        }\n                    }\n                }\n                if (injectCtor == null) {\n                    throw new RuntimeException(\n                            \"Could not determine constructor for \" + theClass + \" add @Inject to a constructor\");\n                }\n            }\n            this.injectCtor = injectCtor;\n        }\n\n        void preWrite(Map<Object, DeferredParameter> parameterMap) {\n            if (injectCtor != null) {\n                try {\n                    java.lang.reflect.Type[] parameterTypes = injectCtor.getGenericParameterTypes();\n                    Annotation[][] parameterAnnotations = injectCtor.getParameterAnnotations();\n                    for (int i = 0; i < parameterTypes.length; i++) {\n                        java.lang.reflect.Type param = parameterTypes[i];\n                        var constantHolder = findConstantForParam(param);\n                        if (constantHolder != null) {\n                            deferredParameters.add(loadObjectInstance(constantHolder.value, parameterMap,\n                                    constantHolder.type, Arrays.stream(parameterAnnotations[i])\n                                            .anyMatch(s -> s.annotationType() == RelaxedValidation.class)));","sourceCodeStart":1775,"sourceCodeEnd":1811,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java#L1775-L1811","documentation":"BytecodeRecorderImpl serializes recorded build-time objects into generated bytecode so they can be re-created at runtime. When it needs to instantiate a recorded class it must pick a constructor; if the class has multiple constructors and none is annotated with @Inject, the recorder cannot decide which to use and throws this RuntimeException at augmentation time.","triggerScenarios":"Recording (via BytecodeRecorderImpl or @Recorder proxies / runtime initialization) a class that has more than one constructor and neither a unique no-arg constructor nor an @Inject-annotated constructor. Typically hit when returning custom objects from @Recorder methods or using RuntimeValue/recorded config objects whose class lacks @Inject.","commonSituations":"Extension authors add a convenience overloaded constructor to a recorded config or recorder-support class and forget to mark the intended one @Inject; upgrading Quarkus changes how classes are recorded; third-party classes with multiple constructors are used inside recorded objects.","solutions":["Annotate exactly one constructor of the class with jakarta.inject.Inject (or javax.inject.Inject)","Reduce the class to a single constructor so the recorder can pick it unambiguously","Provide a no-arg constructor plus setters, or refactor the class into a simple POJO with default constructor","If the class is not yours, wrap it in a small holder class with a single @Inject constructor"],"exampleFix":"// before\nclass MyRecorderConfig {\n    MyRecorderConfig(String name) { ... }\n    MyRecorderConfig(String name, int size) { ... }\n}\n// after\nclass MyRecorderConfig {\n    @Inject\n    MyRecorderConfig(String name) { ... }\n    // removed or delegating second constructor\n}","handlingStrategy":"validation","validationCode":"static boolean hasUnambiguousCtor(Class<?> c) {\n    long injects = java.util.Arrays.stream(c.getDeclaredConstructors())\n        .filter(k -> java.util.Arrays.stream(k.getAnnotations())\n            .anyMatch(a -> a.annotationType().getSimpleName().equals(\"Inject\"))).count();\n    return c.getDeclaredConstructors().length == 1 || injects == 1;\n}","typeGuard":"static boolean hasInjectCtor(Class<?> c) {\n    return java.util.Arrays.stream(c.getDeclaredConstructors())\n        .anyMatch(k -> java.util.Arrays.stream(k.getAnnotations())\n            .anyMatch(a -> a.annotationType().getSimpleName().equals(\"Inject\")));\n}","tryCatchPattern":"try {\n    recorder.returnValue(recorded);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Could not determine constructor\")) {\n        throw new IllegalStateException(\"Add @Inject to exactly one constructor of \" + recorded.getClass(), e);\n    }\n    throw e;\n}","preventionTips":["Give recorded classes exactly one constructor, annotated @Inject","Never add convenience constructors to recorder-support/config classes","Review any constructor addition in classes returned from @Recorder methods"],"tags":["build-time","bytecode-recorder","dependency-injection","quarkus-extension"],"backgroundTag":"no-injectable-constructor","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}