quarkusio/quarkus · error · DefinitionException
Decorator declares a disposer method: ${decoratorClass}
Error message
Decorator declares a disposer method: ${decoratorClass} What it means
Build-time CDI definition error: while walking the decorator's class hierarchy, ArC found a method with a @Disposes parameter. Disposer methods belong to producer classes, not decorators; the decorator class in the message declares one and deployment fails.
Source
Thrown at independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/Decorators.java:168
}
}
checkDecoratorFieldsAndMethods(decoratorClass, beanDeployment);
return new DecoratorInfo(decoratorClass, beanDeployment, delegateInjectionPoint,
decoratedTypes, injections, priority);
}
private static void checkDecoratorFieldsAndMethods(ClassInfo decoratorClass, BeanDeployment beanDeployment) {
ClassInfo aClass = decoratorClass;
while (aClass != null) {
for (MethodInfo method : aClass.methods()) {
if (beanDeployment.hasAnnotation(method, DotNames.PRODUCES)) {
throw new DefinitionException("Decorator declares a producer method: " + decoratorClass);
}
// the following 3 checks rely on the annotation store returning parameter annotations for methods
if (beanDeployment.hasAnnotation(method, DotNames.DISPOSES)) {
throw new DefinitionException("Decorator declares a disposer method: " + decoratorClass);
}
if (beanDeployment.hasAnnotation(method, DotNames.OBSERVES)) {
throw new DefinitionException("Decorator declares an observer method: " + decoratorClass);
}
if (beanDeployment.hasAnnotation(method, DotNames.OBSERVES_ASYNC)) {
throw new DefinitionException("Decorator declares an async observer method: " + decoratorClass);
}
}
for (FieldInfo field : aClass.fields()) {
if (beanDeployment.hasAnnotation(field, DotNames.PRODUCES)) {
throw new DefinitionException("Decorator declares a producer field: " + decoratorClass);
}
}
DotName superClass = aClass.superName();
aClass = superClass != null && !superClass.equals(DotNames.OBJECT)
? getClassByName(beanDeployment.getBeanArchiveIndex(), superClass)View on GitHub (pinned to e1c734241f)
Solutions
- Remove the @Disposes parameter/method from the decorator.
- Move disposal to the bean that declares the corresponding @Produces method.
- Split responsibilities: keep the decorator only for interception logic.
- Audit superclasses, since the check walks the whole hierarchy.
Example fix
// before
@Decorator
public class D implements Greeter {
void dispose(@Disposes Resource r) { r.close(); }
}
// after
@Decorator
public class D implements Greeter { /* no disposer */ } Defensive patterns
Strategy: validation
Validate before calling
if (decoratorClass.getAnnotation(jakarta.decorator.Decorator.class) != null)
for (Method m : decoratorClass.getMethods())
if (Arrays.stream(m.getParameters()).anyMatch(p -> p.isAnnotationPresent(jakarta.enterprise.inject.Disposes.class)))
throw new IllegalStateException("Decorator cannot declare disposer: " + m); Prevention
- Keep disposal in the bean that produces the object, never in a decorator.
- Add an automated rule banning @Disposes inside @Decorator classes.
When it happens
Trigger: A @Decorator class contains a method with a @Disposes-annotated parameter, found during createDecorator via checkDecoratorFieldsAndMethods.
Common situations: Merging disposer logic from a producer bean into a decorator; leftover disposer after converting a class into a decorator; copying a disposer method into the decorator for convenience.
Related errors
- Decorator declares a producer method: ${decoratorClass}
- Decorator declares an observer method: ${decoratorClass}
- Decorator declares an async observer method: ${decoratorClas
- Decorator declares a producer field: ${decoratorClass}
- No producer method or field declared by the bean class that
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/a3d97a97f01435c9.
Report an issue: GitHub.