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
- Remove the outer transaction so the thread has no active tx (use NOT_SUPPORTED or restructure code)
- Change semantics to REQUIRED or REQUIRES_NEW if nesting is intended
- 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
- Call QuarkusTransaction.isActive() before DISALLOW_EXISTING blocks
- Avoid wrapping @Transactional methods with DISALLOW_EXISTING programmatic calls
- Map transactional boundaries explicitly in the service layer
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
- Cannot specify both an exception handler and SUSPEND_EXISTIN
- Transaction already active
- seconds cannot be negative
- Unknown semantics
- Error getting the status of the current transaction
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/03bedda30eb700e0.
Report an issue: GitHub.