quarkusio/quarkus · error · BlockingOperationNotAllowedException

@Transactional cannot start a JTA transaction within a react

Error message

@Transactional cannot start a JTA transaction within a reactive pipeline. If the annotated method is intended to be blocking, ensure it is executed on a worker thread, for example by annotating it with @Blocking. If the annotated method is intended to be reactive, consider using Hibernate Reactive, which supports @Transactional in a reactive context.

What it means

Quarkus throws BlockingOperationNotAllowedException when @Transactional intercepts a method running inside a reactive pipeline (event-loop / IO thread) where blocking JTA operations are forbidden. Starting or joining a JTA transaction blocks the calling thread, which would stall the event loop, so the BlockingOperationControl check rejects it. The message tells you to either run the method on a worker thread or use a reactive transaction API.

Source

Thrown at extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java:90

    protected TransactionalInterceptorBase(boolean userTransactionAvailable) {
        this.userTransactionAvailable = userTransactionAvailable;
    }

    public Object intercept(InvocationContext ic) throws Exception {
        final TransactionManager tm = transactionManager;
        final Transaction tx = tm.getTransaction();

        boolean previousUserTransactionAvailability = setUserTransactionAvailable(userTransactionAvailable);
        try {
            return doIntercept(tm, tx, ic);
        } finally {
            resetUserTransactionAvailability(previousUserTransactionAvailability);
        }
    }

    protected void checkBlockingAllowed() {
        if (!BlockingOperationControl.isBlockingAllowed()) {
            throw new BlockingOperationNotAllowedException(
                    "@Transactional cannot start a JTA transaction within a reactive pipeline." +
                            " If the annotated method is intended to be blocking, ensure it is executed on a worker thread, for example by annotating it with @Blocking."
                            +
                            " If the annotated method is intended to be reactive, consider using Hibernate Reactive, which supports @Transactional in a reactive context.");
        }
    }

    protected abstract Object doIntercept(TransactionManager tm, Transaction tx, InvocationContext ic) throws Exception;

    /**
     * <p>
     * Looking for the {@link Transactional} annotation first on the method,
     * second on the class.
     * <p>
     * Method handles CDI types to cover cases where extensions are used. In
     * case of EE container uses reflection.
     *
     * @param ic invocation context of the interceptor

View on GitHub (pinned to e1c734241f)

Solutions

  1. Annotate the @Transactional method (or its caller) with @Blocking so it runs on a worker thread
  2. Move the transactional logic behind a blocking bean and call it from a Uni created with emitOn/runSubscriptionOn a worker executor
  3. Use Hibernate Reactive (quarkus-hibernate-reactive) with reactive transactions instead of JTA for reactive pipelines
  4. Restructure so event-loop code never directly invokes transactional blocking methods

Example fix

// before
@Transactional
public Order save(Order o) { ... } // called from event loop
// after
@Blocking
@Transactional
public Order save(Order o) { ... }
Defensive patterns

Strategy: validation

Validate before calling

boolean safeToRunTransactional = BlockingOperationControl.isBlockingAllowed();
if (!safeToRunTransactional) { /* route to worker thread or use reactive API */ }

Try / catch

try { service.save(o); } catch (BlockingOperationNotAllowedException e) {
    // re-dispatch on worker thread:
    Uni.createFrom().item(() -> service.save(o)).emitOn(executor).await().indefinitely();
}

Prevention

When it happens

Trigger: Calling a @Transactional method from a RESTEasy Reactive (Vert.x event-loop) endpoint or any reactive callback, without @Blocking and without running on a worker thread; @Transactional applied to a method invoked inside Uni/Multi chains.

Common situations: quarkus-rest (reactive) endpoint method is itself @Transactional and executes on the event loop; calling a blocking @Transactional service from an event-loop handler or a reactive messaging callback; forgetting @Blocking (executeOnWorkerThread) when mixing reactive and blocking code.

Related errors


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