quarkusio/quarkus · error · BlockingOperationNotAllowedException

You have attempted to perform a blocking operation on a IO t

Error message

You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking StatelessSession operations make sure you are doing it from a worker thread.

What it means

Quarkus rejects blocking operations (JPA/Hibernate calls that hit the database) when they run on a Vert.x IO (event-loop) thread, because blocking would stall event-loop processing for all requests. TransactionScopedStatelessSession.checkBlocking() uses BlockingOperationControl to verify the current thread allows blocking before any StatelessSession operation. Hibernate ORM is inherently blocking, so these calls must happen on a worker thread.

Source

Thrown at extensions/hibernate-orm/runtime/src/main/java/io/quarkus/hibernate/orm/runtime/session/TransactionScopedStatelessSession.java:108

                        false, false);
            } else {
                throw new ContextNotActiveException(
                        "Cannot use the StatelessSession because neither a transaction nor a CDI request context is active."
                                + " Consider adding @Transactional to your method to automatically activate a transaction,"
                                + " or @ActivateRequestContext if you have valid reasons not to use transactions.");
            }
        } else {
            throw new ContextNotActiveException(
                    "Cannot use the StatelessSession because no transaction is active."
                            + " Consider adding @Transactional to your method to automatically activate a transaction,"
                            + " or set '" + HibernateOrmRuntimeConfig.extensionPropertyKey("request-scoped.enabled")
                            + "' to 'true' if you have valid reasons not to use transactions.");
        }
    }

    private void checkBlocking() {
        if (!BlockingOperationControl.isBlockingAllowed()) {
            throw new BlockingOperationNotAllowedException(
                    "You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking StatelessSession operations make sure you are doing it from a worker thread.");
        }
    }

    private boolean isInTransaction() {
        try {
            switch (transactionManager.getStatus()) {
                case Status.STATUS_ACTIVE:
                case Status.STATUS_COMMITTING:
                case Status.STATUS_MARKED_ROLLBACK:
                case Status.STATUS_PREPARED:
                case Status.STATUS_PREPARING:
                    return true;
                default:
                    return false;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Annotate the endpoint/method with @Blocking (or remove @NonBlocking) so Quarkus dispatches it to a worker thread
  2. Wrap the blocking StatelessSession usage in Mutiny's Infrastructure.getCurrentVertxWorkerPool().executeBlocking or runInWorkerThread semantics (e.g. Uni.createFrom().item(() -> ...) .runSubscriptionOn(Infrastructure.getDefaultWorkerPool()))
  3. Use a Panache reactive or Hibernate Reactive API instead of the blocking StatelessSession if the code is reactive
  4. Check that no library callback (e.g. security check, filter) marked the execution as non-blocking unexpectedly

Example fix

// before
@GET
@NonBlocking
public Uni<Pet> get(long id) {
    return Uni.createFrom().item(() -> {
        return statelessSession.createQuery(...).uniqueResult(); // throws
    });
}
// after
@GET
@Blocking
public Pet get(long id) {
    return statelessSession.createQuery(...).uniqueResult();
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!BlockingOperationControl.isBlockingAllowed()) {
    // defer to worker thread or throw a friendly error
    return runOnWorkerThread(() -> doHibernateWork());
}

Type guard

boolean canRunBlockingNow() {
    return BlockingOperationControl.isBlockingAllowed();
}

Try / catch

try {
    return statelessSession.createQuery(...).uniqueResult();
} catch (BlockingOperationNotAllowedException e) {
    Log.warn("Blocking Hibernate call on IO thread; rerouting");
    return Infrastructure.getDefaultWorkerPool().executeBlocking(() -> doWork()).await().indefinitely();
}

Prevention

When it happens

Trigger: Calling any StatelessSession method (refresh, createQuery, createNamedQuery, createNativeQuery, createNamedStoredProcedureQuery, createStoredProcedureQuery, insert, update, delete, etc.) from code running on an event-loop thread — e.g. a @NonBlocking annotated endpoint, an unauthenticated blocking=never resource, reactive routes, or a Vert.x worker-less callback.

Common situations: REST endpoint that returns Uni/Multi or is marked non-blocking but still calls blocking Hibernate StatelessSession; calling session code from a reactive pipeline (map/subscribe callbacks) that stays on the IO thread; changing an endpoint from blocking to reactive without moving DB access to a worker thread.

Related errors


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