{"record":{"id":"9bcffef7c21467d1","repo":"quarkusio/quarkus","slug":"transactional-cannot-start-a-jta-transaction-with","errorCode":null,"errorMessage":"@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.","messagePattern":"@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\\.","errorType":"exception","errorClass":"BlockingOperationNotAllowedException","httpStatus":null,"severity":"error","filePath":"extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java","lineNumber":90,"sourceCode":"    protected TransactionalInterceptorBase(boolean userTransactionAvailable) {\n        this.userTransactionAvailable = userTransactionAvailable;\n    }\n\n    public Object intercept(InvocationContext ic) throws Exception {\n        final TransactionManager tm = transactionManager;\n        final Transaction tx = tm.getTransaction();\n\n        boolean previousUserTransactionAvailability = setUserTransactionAvailable(userTransactionAvailable);\n        try {\n            return doIntercept(tm, tx, ic);\n        } finally {\n            resetUserTransactionAvailability(previousUserTransactionAvailability);\n        }\n    }\n\n    protected void checkBlockingAllowed() {\n        if (!BlockingOperationControl.isBlockingAllowed()) {\n            throw new BlockingOperationNotAllowedException(\n                    \"@Transactional cannot start a JTA transaction within a reactive pipeline.\" +\n                            \" If the annotated method is intended to be blocking, ensure it is executed on a worker thread, for example by annotating it with @Blocking.\"\n                            +\n                            \" If the annotated method is intended to be reactive, consider using Hibernate Reactive, which supports @Transactional in a reactive context.\");\n        }\n    }\n\n    protected abstract Object doIntercept(TransactionManager tm, Transaction tx, InvocationContext ic) throws Exception;\n\n    /**\n     * <p>\n     * Looking for the {@link Transactional} annotation first on the method,\n     * second on the class.\n     * <p>\n     * Method handles CDI types to cover cases where extensions are used. In\n     * case of EE container uses reflection.\n     *\n     * @param ic invocation context of the interceptor","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/interceptor/TransactionalInterceptorBase.java#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Annotate the @Transactional method (or its caller) with @Blocking so it runs on a worker thread","Move the transactional logic behind a blocking bean and call it from a Uni created with emitOn/runSubscriptionOn a worker executor","Use Hibernate Reactive (quarkus-hibernate-reactive) with reactive transactions instead of JTA for reactive pipelines","Restructure so event-loop code never directly invokes transactional blocking methods"],"exampleFix":"// before\n@Transactional\npublic Order save(Order o) { ... } // called from event loop\n// after\n@Blocking\n@Transactional\npublic Order save(Order o) { ... }","handlingStrategy":"validation","validationCode":"boolean safeToRunTransactional = BlockingOperationControl.isBlockingAllowed();\nif (!safeToRunTransactional) { /* route to worker thread or use reactive API */ }","typeGuard":null,"tryCatchPattern":"try { service.save(o); } catch (BlockingOperationNotAllowedException e) {\n    // re-dispatch on worker thread:\n    Uni.createFrom().item(() -> service.save(o)).emitOn(executor).await().indefinitely();\n}","preventionTips":["Always pair @Transactional with @Blocking in reactive REST endpoints","Never call blocking beans directly from event-loop handlers or reactive callbacks","Prefer Hibernate Reactive for reactive pipelines needing transactions","Document which service methods are blocking-only"],"tags":["jta","reactive","blocking","event-loop","quarkus"],"backgroundTag":"blocking-operation-not-allowed","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}