quarkusio/quarkus · error · QuarkusTransactionException

Transaction already active

Error message

Transaction already active

What it means

callDisallowExisting implements TransactionSemantics.DISALLOW_EXISTING: if a transaction is already active on the current thread it wraps an IllegalStateException('Transaction already active') in QuarkusTransactionException. DISALLOW_EXISTING requires no active transaction when the task begins.

Source

Thrown at extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransactionImpl.java:94

        if (isTransactionActive()) {
            return callInTheirTx(options, task);
        } else {
            return callInOurTx(options, task);
        }
    }

    private static boolean isTransactionActive() {
        try {
            int status = getUserTransaction().getStatus();
            return status != Status.STATUS_NO_TRANSACTION;
        } catch (SystemException e) {
            throw new QuarkusTransactionException(e);
        }
    }

    private static <T> T callDisallowExisting(RunOptionsBase options, Callable<T> task) {
        if (isTransactionActive()) {
            throw new QuarkusTransactionException(new IllegalStateException("Transaction already active"));
        }
        return callInOurTx(options, task);
    }

    private static <T> T callRequireNew(RunOptionsBase options, Callable<T> task) {
        TransactionManager transactionManager = getTransactionManager();
        Transaction transaction = null;
        try {
            if (isTransactionActive()) {
                transaction = transactionManager.suspend();
            }
            T result = callInOurTx(options, task);
            if (transaction != null) {
                try {
                    transactionManager.resume(transaction);
                    transaction = null;
                } catch (Exception e) {
                    throw new QuarkusTransactionException(e);

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the outer transaction so the thread has no active tx (use NOT_SUPPORTED or restructure code)
  2. Change semantics to REQUIRED or REQUIRES_NEW if nesting is intended
  3. Catch QuarkusTransactionException if the 'no active tx' precondition can legitimately be violated

Example fix

// before
QuarkusTransaction.call(new RunOptions().semantics(TransactionSemantics.DISALLOW_EXISTING), task); // inside @Transactional method
// after
QuarkusTransaction.call(new RunOptions().semantics(TransactionSemantics.REQUIRES_NEW), task);
Defensive patterns

Strategy: try-catch

Validate before calling

if (QuarkusTransaction.isActive() && semantics == TransactionSemantics.DISALLOW_EXISTING) {
    throw new IllegalStateException("Cannot use DISALLOW_EXISTING inside an active transaction");
}

Try / catch

try {
    return QuarkusTransaction.call(new RunOptions().semantics(TransactionSemantics.DISALLOW_EXISTING), task);
} catch (QuarkusTransactionException e) {
    if (e.getCause() instanceof IllegalStateException && e.getCause().getMessage().contains("Transaction already active")) {
        return runWithoutTransaction(task); // or restructure caller
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling QuarkusTransaction.call/run with DISALLOW_EXISTING semantics from within code that already started a transaction (another QuarkusTransaction call, @Transactional method, or manual UserTransaction.begin).

Common situations: Nesting programmatic transactions with DISALLOW_EXISTING inside @Transactional service methods; transaction interceptor still active on the call path (e.g. CDI interceptor not suspended).

Related errors


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