{"record":{"id":"72ce2c30eaf1e15b","repo":"quarkusio/quarkus","slug":"bean-constructor-must-not-have-a-disposes-paramet","errorCode":null,"errorMessage":"Bean constructor must not have a @Disposes parameter: ","messagePattern":"Bean constructor must not have a @Disposes parameter: ","errorType":"validation","errorClass":"DefinitionException","httpStatus":null,"severity":"error","filePath":"independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Injection.java","lineNumber":230,"sourceCode":"    static List<Injection> forBean(AnnotationTarget beanTarget, BeanInfo declaringBean, BeanDeployment beanDeployment,\n            InjectionPointModifier transformer, BeanType beanType) {\n        if (Kind.CLASS.equals(beanTarget.kind())) {\n            List<Injection> injections = forClassBean(beanTarget.asClass(), beanTarget.asClass(), beanDeployment,\n                    transformer, false, new HashSet<>());\n\n            Set<AnnotationTarget> injectConstructors = injections.stream().filter(Injection::isConstructor)\n                    .map(Injection::getTarget).collect(Collectors.toSet());\n            if (injectConstructors.size() > 1) {\n                throw new DefinitionException(\n                        \"Multiple @Inject constructors found on \" + beanTarget.asClass().name() + \":\\n\"\n                                + injectConstructors.stream().map(Object::toString).collect(Collectors.joining(\"\\n\")));\n            }\n            for (AnnotationTarget injectConstructor : injectConstructors) {\n                Set<AnnotationInstance> parameterAnnotations = Annotations.getParameterAnnotations(beanDeployment,\n                        injectConstructor.asMethod());\n                for (AnnotationInstance annotation : parameterAnnotations) {\n                    if (DotNames.DISPOSES.equals(annotation.name())) {\n                        throw new DefinitionException(\n                                \"Bean constructor must not have a @Disposes parameter: \" + injectConstructor);\n                    }\n                    if (DotNames.OBSERVES.equals(annotation.name())) {\n                        throw new DefinitionException(\n                                \"Bean constructor must not have an @Observes parameter: \" + injectConstructor);\n                    }\n                    if (DotNames.OBSERVES_ASYNC.equals(annotation.name())) {\n                        throw new DefinitionException(\n                                \"Bean constructor must not have an @ObservesAsync parameter: \" + injectConstructor);\n                    }\n                }\n            }\n\n            Set<MethodInfo> initializerMethods = injections.stream()\n                    .filter(it -> it.isMethod() && !it.isConstructor())\n                    .map(Injection::getTarget)\n                    .map(AnnotationTarget::asMethod)\n                    .collect(Collectors.toSet());","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Injection.java#L212-L248","documentation":"ArC (Quarkus's CDI implementation) throws this DefinitionException at build time when a bean's constructor has a parameter annotated with @Disposes. Per the CDI specification, @Disposes parameters belong only on disposer methods; a bean constructor can only have injection points. The check lives in Injection.forBean which validates all annotations on bean constructors.","triggerScenarios":"Declaring a @Inject (bean) constructor whose parameter list contains a parameter annotated @jakarta.enterprise.inject.Disposes (or javax variant). ArC collects the constructor's parameter annotations in forBean and fails validation before bean metadata is generated.","commonSituations":"Copy-pasting a disposer method signature into a constructor; IDE auto-import picking @Disposes when wiring disposal logic; misunderstanding that the container can infer disposal from the constructor; migrating code where a dispose method was merged into a constructor.","solutions":["Remove the @Disposes annotation from the bean constructor parameter","Create a proper disposer: a @Produces method in the same class that creates the resource, and a separate @Disposes method taking the produced object","Keep the constructor parameter as a plain injection point if disposal is not actually needed","Rebuild with mvn compile; the failure occurs during Quarkus augmentation so fix source and rebuild"],"exampleFix":"// before\n@ApplicationScoped\nclass DbBean {\n    DbBean(@Disposes DbConnection conn) { ... }\n}\n\n// after\n@ApplicationScoped\nclass DbBean {\n    private final DbConnection conn;\n    DbBean(DbConnection conn) { this.conn = conn; }\n\n    @Produces\n    DbConnection connect() { return open(); }\n\n    void dispose(@Disposes DbConnection conn) { conn.close(); }\n}","handlingStrategy":"validation","validationCode":"// Build-time augmentation failure: validate annotations before building\nvoid checkConstructor(Method ctor) throws Exception {\n    for (var p : ctor.getParameters()) {\n        if (p.isAnnotationPresent(jakarta.enterprise.inject.Disposes.class)) {\n            throw new IllegalStateException(\"@Disposes on constructor param: \" + ctor);\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never put lifecycle annotations (@Disposes, @Observes, @ObservesAsync) on constructor parameters","Pair every @Produces method with a separate @Disposes method in the same class","Run ./mvnw compile frequently so augmentation errors surface immediately","Use a CDI spec linter or IDE CDI support to flag illegal annotations early"],"tags":["cdi","arc","bean-definition","build-time","disposes"],"backgroundTag":"cdi-bean-definition-invalid","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"}