quarkusio/quarkus · error · IllegalStateException
%s: The class contains a method annotated with @LRA and no m
Error message
%s: The class contains a method annotated with @LRA and no method annotated with @Compensate or @AfterLRA was found.
What it means
Narayana LRA (Long Running Actions) requires that any class participating in an LRA transaction — i.e. containing a method annotated with @LRA — also declares a compensation hook: a method annotated with @Compensate or @AfterLRA. During deployment, NarayanaLRAProcessor.isLRAParticipant scans the class hierarchy for these annotations and throws IllegalStateException at build time if a compensation method is missing, because the participant could never complete or compensate the LRA.
Source
Thrown at extensions/narayana-lra/deployment/src/main/java/io/quarkus/narayana/lra/deployment/NarayanaLRAProcessor.java:102
int modifiers = classInfo.flags();
if (Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers) || !isLRAParticipant(index, classInfo)) {
continue;
}
classNames.add(classInfo.toString());
}
recorder.setParticipantTypes(classNames);
}
private boolean isLRAParticipant(IndexView index, ClassInfo classInfo) {
Map<DotName, List<AnnotationInstance>> annotations = getAllAnnotationsFromClassInfoHierarchy(classInfo.name(), index);
if (!annotations.containsKey(DotNames.LRA)) {
return false;
} else if (!annotations.containsKey(DotNames.COMPENSATE) && !annotations.containsKey(DotNames.AFTER_LRA)) {
throw new IllegalStateException(String.format("%s: %s",
classInfo.name(),
"The class contains a method annotated with @LRA and no method annotated with @Compensate or @AfterLRA was found."));
} else {
return true;
}
}
private Map<DotName, List<AnnotationInstance>> getAllAnnotationsFromClassInfoHierarchy(DotName name,
IndexView index) {
Map<DotName, List<AnnotationInstance>> annotations = new HashMap<>();
if (name == null || name.equals(DotNames.OBJECT)) {
return annotations;
}
ClassInfo classInfo = index.getClassByName(name);
if (classInfo != null) {View on GitHub (pinned to e1c734241f)
Solutions
- Add a method annotated with @Compensate to the class (or a superclass) that undoes the work done by the @LRA method.
- Alternatively annotate a method with @AfterLRA to receive the LRA completion callback if compensation is not required.
- If the class should not be a participant at all, remove the @LRA annotation from its methods.
- Ensure the compensating method is on the same class hierarchy as the @LRA method — annotations on unrelated classes are not picked up.
Example fix
// before
public class BookingResource {
@LRA(value = Type.REQUIRED)
@Path("/book")
public Response book() { ... }
}
// after
public class BookingResource {
@LRA(value = Type.REQUIRED)
@Path("/book")
public Response book() { ... }
@Compensate
public Response compensate() { ... /* undo booking */ }
} Defensive patterns
Strategy: validation
Validate before calling
boolean hasLRA = methods.stream().anyMatch(m -> m.isAnnotationPresent(LRA.class));
boolean hasCompensation = methods.stream().anyMatch(m ->
m.isAnnotationPresent(Compensate.class) || m.isAnnotationPresent(AfterLRA.class));
if (hasLRA && !hasCompensation) {
throw new IllegalStateException("@LRA class must declare a @Compensate or @AfterLRA method: " + cls.getName());
} Prevention
- Always pair every @LRA method with a @Compensate or @AfterLRA method in the same class
- Add an ArchUnit or annotation-scan test that enforces the pairing across the codebase
- Keep compensation methods on the same class hierarchy as the @LRA method
When it happens
Trigger: A class with a method annotated with @LRA (in the class or inherited from its hierarchy) has no method annotated with @Compensate and no method annotated with @AfterLRA anywhere in that hierarchy; detected during Quarkus augmentation when createLRAParticipantRegistry indexes LRA participant classes.
Common situations: Writing a first LRA resource and forgetting the compensating method; migrating code from an annotation-based LRA framework with different compensation conventions; refactoring that removes or renames the @Compensate/@AfterLRA method while leaving the @LRA method in place; copy-pasting an example with only the @LRA method.
Related errors
- bean must be annotated with @io.smallrye.common.annotation
- A generic type is not allowed here; try creating a subclass
- Build item class must be leaf (final) types: %s
- Cannot construct empty build items
- Failed to open path tree with root %s
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/420c305cc508db25.
Report an issue: GitHub.