quarkusio/quarkus · error · UnsupportedOperationException
Calling a method annotated with @Transactional from a method
Error message
Calling a method annotated with @Transactional from a method annotated with @ReactiveTransactional is not supported. Use either @Transactional or @WithSessionOnDemand/@WithSession/@WithTransaction, but not both, throughout your whole application.
What it means
The @Transactional interceptor detects a REACTIVE_TRANSACTIONAL_METHOD_KEY flag, meaning a @ReactiveTransactional method is up the call stack. Mixing the legacy @ReactiveTransactional annotation with plain @Transactional is unsupported and throws UnsupportedOperationException.
Source
Thrown at extensions/reactive-transactions/runtime/src/main/java/io/quarkus/reactive/transaction/runtime/TransactionalInterceptorBase.java:286
protected void validateLegacyPanacheAnnotations() {
// We are running on the retrieved context, however, the method also switch the safety flag.
Context ignored = vertxContext();
if (ContextLocals.get(SESSION_ON_DEMAND_KEY).isPresent()) {
throw new UnsupportedOperationException(
"Calling a method annotated with @Transactional from a method annotated with @WithSessionOnDemand is not supported. "
+ "Use either @Transactional or @WithSessionOnDemand/@WithSession/@WithTransaction, "
+ "but not both, throughout your whole application.");
}
if (ContextLocals.get(WITH_TRANSACTION_METHOD_KEY).isPresent()) {
throw new UnsupportedOperationException(
"Calling a method annotated with @Transactional from a method annotated with @WithTransaction is not supported. "
+ "Use either @Transactional or @WithSessionOnDemand/@WithSession/@WithTransaction, "
+ "but not both, throughout your whole application.");
}
if (ContextLocals.get(REACTIVE_TRANSACTIONAL_METHOD_KEY).isPresent()) {
throw new UnsupportedOperationException(
"Calling a method annotated with @Transactional from a method annotated with @ReactiveTransactional is not supported. "
+ "Use either @Transactional or @WithSessionOnDemand/@WithSession/@WithTransaction, "
+ "but not both, throughout your whole application.");
}
}
/**
*
* @return the current vertx duplicated context
* @throws IllegalStateException If no vertx context is found or is not a safe context as mandated by the
* {@link VertxContextSafetyToggle}
*/
private static Context vertxContext() {
Context context = Vertx.currentContext();
if (context != null) {
VertxContextSafetyToggle.validateContextIfExists(ERROR_MSG, ERROR_MSG);
return context;
} else {View on GitHub (pinned to e1c734241f)
Solutions
- Use @Transactional consistently on both methods (the reactive interceptor handles Uni-returning methods).
- Alternatively keep @ReactiveTransactional on both, but never nest one inside the other.
- Add an architecture test to enforce a single transactional annotation style.
Example fix
// before
@ReactiveTransactional
public Uni<Item> outer() { return inner(); }
@Transactional
public Uni<Item> inner() { ... }
// after
@Transactional
public Uni<Item> outer() { return inner(); }
@Transactional
public Uni<Item> inner() { ... } Defensive patterns
Strategy: validation
Validate before calling
void check(Class<?> bean) {
for (Method m : bean.getDeclaredMethods())
if (m.isAnnotationPresent(ReactiveTransactional.class) && callsTransactionalMethods(bean, m))
throw new IllegalStateException("@ReactiveTransactional wrapping @Transactional: " + m);
} Try / catch
try {
return service.outer();
} catch (UnsupportedOperationException e) {
log.error("Do not nest @ReactiveTransactional with @Transactional: {}", e.getMessage());
throw e;
} Prevention
- Standardize on @Transactional for reactive Uni methods
- Never nest @ReactiveTransactional and @Transactional in one call chain
- Add architecture tests enforcing one annotation style
- During migration, migrate whole call chains at once
When it happens
Trigger: Calling a @Transactional-annotated method from a method annotated with @ReactiveTransactional (the two interceptors cannot safely nest).
Common situations: Code migrated partially from @ReactiveTransactional to @Transactional; developers assuming both are equivalent and combining them.
Related errors
- Calling a method annotated with @Transactional from a method
- Calling a method annotated with @Transactional from a method
- Unable to load repository/entity class mapping ->
- Your repository class was not properly detected and assigne
- Entity '%s' was not found. Did you forget to annotate your P
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/b6bf76c239d53f88.
Report an issue: GitHub.