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 @WithTransaction is not supported. Use either @Transactional or @WithSessionOnDemand/@WithSession/@WithTransaction, but not both, throughout your whole application.

What it means

Same family as above: the @Transactional interceptor detects a WITH_TRANSACTION_METHOD_KEY flag left by a legacy @WithTransaction method up the call stack. Mixing legacy Panache @WithTransaction with @Transactional in one call chain is unsupported and throws UnsupportedOperationException.

Source

Thrown at extensions/reactive-transactions/runtime/src/main/java/io/quarkus/reactive/transaction/runtime/TransactionalInterceptorBase.java:279

    public static boolean reactiveInterceptorShouldRun() {
        boolean condition = Context.isOnEventLoopThread();
        LOG.tracef("Transactional interceptor should run: %s", condition);
        return condition;
    }

    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}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Replace @WithTransaction with @ReactiveTransactional (or plain @Transactional) on the caller.
  2. Ensure only one transactional annotation family is used across the whole application per the error message.
  3. Audit call graphs so legacy Panache annotations never wrap @Transactional methods.

Example fix

// before
@WithTransaction
public Uni<Order> save(Order o) { return repo.persist(o); }
// after
@ReactiveTransactional
public Uni<Order> save(Order o) { return repo.persist(o); }
Defensive patterns

Strategy: validation

Validate before calling

void check(Class<?> bean) {
    for (Method m : bean.getDeclaredMethods())
        if (m.isAnnotationPresent(WithTransaction.class) && usesTransactionalServices(bean, m))
            throw new IllegalStateException("@WithTransaction wrapping @Transactional: " + m);
}

Try / catch

try {
    return service.save(order);
} catch (UnsupportedOperationException e) {
    log.error("Replace legacy @WithTransaction with @ReactiveTransactional: {}", e.getMessage());
    throw e;
}

Prevention

When it happens

Trigger: Calling a @Transactional-annotated method from a method annotated with Panache's @WithTransaction.

Common situations: Incremental migration from @WithTransaction to @Transactional; utility methods still using @WithTransaction while services migrated to @Transactional.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/c750200b2d4fe899. Report an issue: GitHub.