{"record":{"id":"8b3d016717991d44","repo":"quarkusio/quarkus","slug":"synthetic-observer-declared-as-asynchronous-and-tr","errorCode":null,"errorMessage":"Synthetic observer declared as asynchronous and transactional (event type \" + syntheticObserver.type + \", \\\"declared\\\" by \" + syntheticObserver.declaringClass + \", notified using \" + syntheticObserver.implementationClass + \")","messagePattern":"Synthetic observer declared as asynchronous and transactional \\(event type \" \\+ syntheticObserver\\.type \\+ \", \\\\\"declared\\\\\" by \" \\+ syntheticObserver\\.declaringClass \\+ \", notified using \" \\+ syntheticObserver\\.implementationClass \\+ \"\\)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/bcextensions/ExtensionsEntryPoint.java","lineNumber":482,"sourceCode":"            }\n            bean.done();\n        }\n    }\n\n    /**\n     * Must be called <i>after</i> {@code runSynthesis} and <i>before</i> {@code runRegistrationAgain}.\n     * <p>\n     * It is a no-op if no {@link BuildCompatibleExtension} was found.\n     */\n    public void registerSyntheticObservers(ObserverRegistrar.RegistrationContext context,\n            Predicate<DotName> isApplicationClass) {\n        if (invoker.isEmpty()) {\n            return;\n        }\n\n        for (SyntheticObserverBuilderImpl<?> syntheticObserver : syntheticObservers) {\n            if (syntheticObserver.isAsync && syntheticObserver.transactionPhase != TransactionPhase.IN_PROGRESS) {\n                throw new IllegalStateException(\"Synthetic observer declared as asynchronous and transactional \"\n                        + \"(event type \" + syntheticObserver.type + \", \\\"declared\\\" by \" + syntheticObserver.declaringClass\n                        + \", notified using \" + syntheticObserver.implementationClass + \")\");\n            }\n\n            ObserverConfigurator observer = context.configure()\n                    .beanClass(syntheticObserver.declaringClass)\n                    .observedType(syntheticObserver.type)\n                    .qualifiers(syntheticObserver.qualifiers.toArray(new org.jboss.jandex.AnnotationInstance[0]))\n                    .priority(syntheticObserver.priority)\n                    .async(syntheticObserver.isAsync)\n                    .transactionPhase(syntheticObserver.transactionPhase);\n            configureParams(observer, syntheticObserver.params);\n            observer.notify(ng -> {\n                BlockCreator bc = ng.notifyMethod();\n\n                // | SyntheticObserver instance = new ConfiguredEventConsumer();\n                Expr instance = bc.new_(syntheticObserver.implementationClass);\n","sourceCodeStart":464,"sourceCodeEnd":500,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/bcextensions/ExtensionsEntryPoint.java#L464-L500","documentation":"Arc rejects synthetic observers that are declared both asynchronous and transactional: an observer registered via SyntheticObserverBuilderImpl with isAsync set and a transactionPhase other than TransactionPhase.IN_PROGRESS is contradictory, because transactional observer semantics only apply to synchronous notification. ExtensionsEntryPoint.registerSyntheticObservers() throws IllegalStateException during deployment to surface the misconfiguration early.","triggerScenarios":"Calling SyntheticObserverBuilder.configureAsync().observeAsync() (or the equivalent flag) together with a transaction phase such as BEFORE_COMPLETION/AFTER_COMPLETION/AFTER_FAILURE/AFTER_SUCCESS on the same synthetic observer, then registering it from a build-time extension.","commonSituations":"Upgrading an extension that previously used only async observers and adding transaction phases; copying configurator code from a synchronous transactional observer; misunderstanding that @ObservesAsync and @Observes(during=...) are mutually exclusive styles.","solutions":["Remove the transaction phase configuration and keep the observer asynchronous (IN_PROGRESS is the only accepted combination).","Or drop the async configuration if the observer must run in a transaction phase.","Audit all SyntheticObserverBuilder usages in the extension for the async + transactionPhase combination."],"exampleFix":"// before\nbuilder.configureAsync().observeAsync().transactionPhase(TransactionPhase.BEFORE_COMPLETION);\n\n// after (choose one)\nbuilder.configureAsync().observeAsync(); // async, non-transactional\n// or\nbuilder.transactionPhase(TransactionPhase.BEFORE_COMPLETION); // synchronous transactional","handlingStrategy":"validation","validationCode":"if (builder.isAsync() && builder.getTransactionPhase() != TransactionPhase.IN_PROGRESS) {\n    throw new IllegalStateException(\"Synthetic observer cannot be async and transactional\");\n}","typeGuard":"static boolean isLegalObserverConfig(boolean async, TransactionPhase phase) {\n    return !async || phase == TransactionPhase.IN_PROGRESS;\n}","tryCatchPattern":"try {\n    extension.registerSyntheticObservers(ctx);\n} catch (IllegalStateException e) {\n    fail(\"Invalid synthetic observer configuration: \" + e.getMessage());\n}","preventionTips":["Treat async and transaction-phase observer configuration as mutually exclusive.","Search extension code for configureAsync/observeAsync and transactionPhase co-usage.","Centralize synthetic-observer registration in one helper that validates the combination once."],"tags":["arc","cdi","events","synthetic-observer","quarkus"],"backgroundTag":"conflicting-observer-declaration","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}