{"record":{"id":"b959e29f1224f26f","repo":"quarkusio/quarkus","slug":"multiple-predestroy-interceptor-methods-declared","errorCode":null,"errorMessage":"Multiple @PreDestroy interceptor methods declared on class: ","messagePattern":"Multiple @PreDestroy interceptor methods declared on class: ","errorType":"validation","errorClass":"DefinitionException","httpStatus":null,"severity":"error","filePath":"independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java","lineNumber":142,"sourceCode":"                            InterceptorPlacement.INTERCEPTOR_CLASS);\n                    if (++aroundConstructsFound > 1) {\n                        throw new DefinitionException(\n                                \"Multiple @AroundConstruct interceptor methods declared on class: \" + aClass);\n                    }\n                }\n                if (store.hasAnnotation(method, DotNames.POST_CONSTRUCT)) {\n                    addInterceptorMethod(allMethods, postConstructs, method, InterceptionType.POST_CONSTRUCT,\n                            InterceptorPlacement.INTERCEPTOR_CLASS);\n                    if (++postConstructsFound > 1) {\n                        throw new DefinitionException(\n                                \"Multiple @PostConstruct interceptor methods declared on class: \" + aClass);\n                    }\n                }\n                if (store.hasAnnotation(method, DotNames.PRE_DESTROY)) {\n                    addInterceptorMethod(allMethods, preDestroys, method, InterceptionType.PRE_DESTROY,\n                            InterceptorPlacement.INTERCEPTOR_CLASS);\n                    if (++preDestroysFound > 1) {\n                        throw new DefinitionException(\n                                \"Multiple @PreDestroy interceptor methods declared on class: \" + aClass);\n                    }\n                }\n                allMethods.add(method);\n            }\n\n            for (FieldInfo field : aClass.fields()) {\n                if (store.hasAnnotation(field, DotNames.PRODUCES)) {\n                    // according to spec, finding @Produces on a field is a DefinitionException\n                    throw new DefinitionException(\n                            \"An interceptor field cannot be marked @Produces - \" + field + \" in class: \" + aClass);\n                }\n            }\n\n            DotName superTypeName = aClass.superName();\n            aClass = superTypeName == null || DotNames.OBJECT.equals(superTypeName) ? null\n                    : getClassByName(beanDeployment.getBeanArchiveIndex(), superTypeName);\n        }","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/InterceptorInfo.java#L124-L160","documentation":"ArC throws this DefinitionException when an interceptor class (or any single class in its hierarchy) declares more than one non-static method annotated with @PreDestroy. The CDI specification allows at most one @PreDestroy lifecycle callback interceptor method per interceptor class. This is detected at build time during bean discovery and aborts deployment.","triggerScenarios":"Two or more non-static @PreDestroy-annotated methods in the same interceptor class. One @PreDestroy per class is allowed across the hierarchy (superclass and subclass each may have one).","commonSituations":"Merging cleanup logic from two interceptors into one class; copy-paste when subclassing an interceptor; adding a second cleanup method without removing the old annotation.","solutions":["Keep one @PreDestroy method per class and delegate to other cleanup methods as plain (unannotated) helpers.","Remove the @PreDestroy annotation from the redundant method.","Split the interceptor into separate classes if the cleanup concerns are independent."],"exampleFix":"// before\nclass MyInterceptor {\n    @PreDestroy void close1(InvocationContext ctx) throws Exception { ctx.proceed(); }\n    @PreDestroy void close2(InvocationContext ctx) throws Exception { ctx.proceed(); }\n}\n// after\nclass MyInterceptor {\n    @PreDestroy void close(InvocationContext ctx) throws Exception { cleanup(); ctx.proceed(); }\n    void cleanup() { /* ... */ }\n}","handlingStrategy":"validation","validationCode":"long n = java.util.Arrays.stream(MyInterceptor.class.getDeclaredMethods())\n    .filter(m -> m.isAnnotationPresent(jakarta.annotation.PreDestroy.class))\n    .count();\nif (n > 1) throw new IllegalStateException(\"Only one @PreDestroy allowed per interceptor class\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["One @PreDestroy per class; delegate to unannotated cleanup helpers","When merging interceptors, consolidate cleanup logic immediately","Include superclass methods in any annotation audit"],"tags":["cdi","quarkus","arc","interceptor","lifecycle"],"backgroundTag":"multiple-interceptor-methods","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"}