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

  1. Remove the exceptionHandler when using SUSPEND_EXISTING
  2. Switch semantics to one that supports exception handlers (e.g. REQUIRED/REQUIRES_NEW)
  3. 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

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


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