quarkusio/quarkus · error · IllegalStateException
Cannot specify both an exception handler and SUSPEND_EXISTIN
Error message
Cannot specify both an exception handler and SUSPEND_EXISTING
What it means
callSuspendExisting (used for TransactionSemantics.SUSPEND_EXISTING) throws IllegalStateException when both an exception handler and SUSPEND_EXISTING semantics are configured. Suspending the existing transaction means exceptions propagate to manage the outer transaction, which is incompatible with intercepting exceptions via a handler.
Source
Thrown at extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/QuarkusTransactionImpl.java:42
private static UserTransaction cachedUserTransaction;
public static <T> T call(RunOptionsBase options, Callable<T> task) {
switch (options.semantics) {
case REQUIRE_NEW:
return callRequireNew(options, task);
case DISALLOW_EXISTING:
return callDisallowExisting(options, task);
case JOIN_EXISTING:
return callJoinExisting(options, task);
case SUSPEND_EXISTING:
return callSuspendExisting(options, task);
}
throw new IllegalArgumentException("Unknown semantics");
}
private static <T> T callSuspendExisting(RunOptionsBase options, Callable<T> task) {
if (options.exceptionHandler != null) {
throw new IllegalStateException("Cannot specify both an exception handler and SUSPEND_EXISTING");
}
TransactionManager transactionManager = getTransactionManager();
Transaction transaction = null;
try {
if (isTransactionActive()) {
transaction = transactionManager.suspend();
}
T result = task.call();
if (transaction != null) {
try {
transactionManager.resume(transaction);
transaction = null;
} catch (Exception e) {
throw new QuarkusTransactionException(e);
}
}
return result;
} catch (Exception e) {View on GitHub (pinned to e1c734241f)
Solutions
- Remove the exceptionHandler when using SUSPEND_EXISTING
- Switch semantics to one that supports exception handlers (e.g. REQUIRED/REQUIRES_NEW)
- Handle exceptions around the QuarkusTransaction.call yourself instead of via the handler option
Example fix
// before QuarkusTransaction.call(new RunOptions().runOptions().exceptionHandler(handler), TaskSemantics.SUSPEND_EXISTING ...) // after QuarkusTransaction.call(new RunOptions(), task); // SUSPEND_EXISTING, no handler // or handle exceptions outside the call
Defensive patterns
Strategy: validation
Validate before calling
if (options.exceptionHandler != null && semantics == TransactionSemantics.SUSPEND_EXISTING) {
throw new IllegalStateException("exceptionHandler cannot be combined with SUSPEND_EXISTING");
} Try / catch
try {
QuarkusTransaction.call(runOptions, task);
} catch (IllegalStateException e) {
if (e.getMessage().contains("exception handler and SUSPEND_EXISTING")) {
// retry without the handler
} else throw e;
} Prevention
- Never set exceptionHandler with SUSPEND_EXISTING semantics
- Read the Javadoc of RunOptionsBase.exceptionHandler for supported semantics
- Centralize RunOptions construction in one utility to enforce the rule
When it happens
Trigger: Building RunOptions/RunOptionsBase with .exceptionHandler(...) and running via QuarkusTransaction.call/run with TransactionSemantics.SUSPEND_EXISTING.
Common situations: Combining a retry/exception-handler option copied from REQUIRED-style options with the SUSPEND_EXISTING semantics; misconfigured programmatic transaction runners.
Related errors
- Transaction already active
- 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/650b6dfca732ca88.
Report an issue: GitHub.