{"record":{"id":"420c305cc508db25","repo":"quarkusio/quarkus","slug":"s-the-class-contains-a-method-annotated-with-lr","errorCode":null,"errorMessage":"%s: The class contains a method annotated with @LRA and no method annotated with @Compensate or @AfterLRA was found.","messagePattern":"(.+?): The class contains a method annotated with @LRA and no method annotated with @Compensate or @AfterLRA was found\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"extensions/narayana-lra/deployment/src/main/java/io/quarkus/narayana/lra/deployment/NarayanaLRAProcessor.java","lineNumber":102,"sourceCode":"            int modifiers = classInfo.flags();\n\n            if (Modifier.isInterface(modifiers) || Modifier.isAbstract(modifiers) || !isLRAParticipant(index, classInfo)) {\n                continue;\n            }\n\n            classNames.add(classInfo.toString());\n        }\n\n        recorder.setParticipantTypes(classNames);\n    }\n\n    private boolean isLRAParticipant(IndexView index, ClassInfo classInfo) {\n        Map<DotName, List<AnnotationInstance>> annotations = getAllAnnotationsFromClassInfoHierarchy(classInfo.name(), index);\n\n        if (!annotations.containsKey(DotNames.LRA)) {\n            return false;\n        } else if (!annotations.containsKey(DotNames.COMPENSATE) && !annotations.containsKey(DotNames.AFTER_LRA)) {\n            throw new IllegalStateException(String.format(\"%s: %s\",\n                    classInfo.name(),\n                    \"The class contains a method annotated with @LRA and no method annotated with @Compensate or @AfterLRA was found.\"));\n        } else {\n            return true;\n        }\n    }\n\n    private Map<DotName, List<AnnotationInstance>> getAllAnnotationsFromClassInfoHierarchy(DotName name,\n            IndexView index) {\n        Map<DotName, List<AnnotationInstance>> annotations = new HashMap<>();\n\n        if (name == null || name.equals(DotNames.OBJECT)) {\n            return annotations;\n        }\n\n        ClassInfo classInfo = index.getClassByName(name);\n\n        if (classInfo != null) {","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/narayana-lra/deployment/src/main/java/io/quarkus/narayana/lra/deployment/NarayanaLRAProcessor.java#L84-L120","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npublic class BookingResource {\n    @LRA(value = Type.REQUIRED)\n    @Path(\"/book\")\n    public Response book() { ... }\n}\n// after\npublic class BookingResource {\n    @LRA(value = Type.REQUIRED)\n    @Path(\"/book\")\n    public Response book() { ... }\n\n    @Compensate\n    public Response compensate() { ... /* undo booking */ }\n}","handlingStrategy":"validation","validationCode":"boolean hasLRA = methods.stream().anyMatch(m -> m.isAnnotationPresent(LRA.class));\nboolean hasCompensation = methods.stream().anyMatch(m ->\n    m.isAnnotationPresent(Compensate.class) || m.isAnnotationPresent(AfterLRA.class));\nif (hasLRA && !hasCompensation) {\n    throw new IllegalStateException(\"@LRA class must declare a @Compensate or @AfterLRA method: \" + cls.getName());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["quarkus","narayana-lra","microservice-transactions","missing-annotation"],"backgroundTag":"missing-compensate-annotation","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"}