{"record":{"id":"3f4997efbed2a396","repo":"quarkusio/quarkus","slug":"cannot-inject-object-of-type-param","errorCode":null,"errorMessage":"Cannot inject object of type ${param}","messagePattern":"Cannot inject object of type (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java","lineNumber":1818,"sourceCode":"        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)));\n                            continue;\n                        }\n\n                        if (param instanceof Class<?>) {\n                            var result = findLoaded(null, (Class<?>) param);\n                            if (result == null) {\n                                throw new RuntimeException(\"Cannot inject object of type \" + param);\n                            }\n                            deferredParameters.add(result);\n                        } else if (param instanceof ParameterizedType paramType\n                                && paramType.getRawType() == RuntimeValue.class) {\n                            if (staticInit) {\n                                deferredParameters.add(new DeferredParameter() {\n                                    @Override\n                                    ResultHandle doLoad(MethodContext context, MethodCreator method, ResultHandle array) {\n                                        return method.newInstance(MethodDescriptor.ofConstructor(RuntimeValue.class));\n                                    }\n                                });\n                            } else {\n                                var result = findLoaded(null, (Class<?>) paramType.getActualTypeArguments()[0]);\n                                if (result == null) {\n                                    throw new RuntimeException(\"Cannot inject object of type \" + param);\n                                }\n                                deferredParameters.add(new DeferredParameter() {\n                                    @Override","sourceCodeStart":1800,"sourceCodeEnd":1836,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java#L1800-L1836","documentation":"BytecodeRecorderImpl can satisfy recorder constructor parameters that are Class or simple known types only if an object of that exact type was previously loaded/recorded in the same recorder context (findLoaded). When a constructor parameter of raw type Class has no corresponding recorded instance, it throws this RuntimeException at augmentation time.","triggerScenarios":"An @Inject-annotated constructor of a recorded class declares a parameter typed as Class<?> (or another injectable raw type) and no value of that type has been recorded into the BytecodeRecorder before the object is created.","commonSituations":"Extension authors add a Class-typed parameter to a recorded object's constructor expecting it to be auto-wired; refactoring a recorder support class without registering the parameter value via recorder methods (e.g. recorder.registerParameters / returned recorded objects).","solutions":["Remove the Class-typed parameter and pass the value explicitly through a recorded method call instead","Record/register the required object in the recorder context before the class is instantiated","Change the constructor to accept only types the recorder can handle (primitives, String, Class with a recorded value, known config types)","Store the class name as a String and resolve it at runtime"],"exampleFix":"// before\n@Inject\nMyRecorderConfig(Class<?> type) { this.type = type; }\n// after\n@Inject\nMyRecorderConfig(String typeName) { this.typeName = typeName; } // resolve Class.forName at runtime","handlingStrategy":"validation","validationCode":"static boolean ctorOnlyInjectableRawClassParams(Class<?> c) {\n    return java.util.Arrays.stream(c.getDeclaredConstructors())\n        .filter(k -> java.util.Arrays.stream(k.getAnnotations())\n            .anyMatch(a -> a.annotationType().getSimpleName().equals(\"Inject\")))\n        .allMatch(k -> java.util.Arrays.stream(k.getParameterTypes())\n            .noneMatch(p -> p == Class.class));\n}","typeGuard":null,"tryCatchPattern":"try {\n    recorderMethod.invoke(...);\n} catch (RuntimeException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Cannot inject object of type\")) {\n        throw new IllegalStateException(\"Register the value in the recorder before instantiation\", e);\n    }\n    throw e;\n}","preventionTips":["Avoid Class-typed constructor parameters in recorded classes","Pass class names as String and resolve with Class.forName at runtime","Record/register every object a constructor needs before instantiation in the same recorder"],"tags":["build-time","bytecode-recorder","dependency-injection","quarkus-extension"],"backgroundTag":"unrecorded-constructor-parameter","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}