quarkusio/quarkus · error · IllegalStateException

Syncs are not allowed to be registered when the transaction

Error message

Syncs are not allowed to be registered when the transaction is in state 

What it means

Quarkus's TSR registerInterposedSynchronization only accepts new interposed synchronizations while the transaction is STATUS_ACTIVE or STATUS_PREPARING. Any other state (e.g. PREPARED, COMMITTING, ROLLED_BACK, ROLLEDBACK, COMMITTED) is too late, and the code throws this IllegalStateException, appending the offending status to the message. It prevents silent loss of cleanup callbacks that would never run.

Source

Thrown at extensions/narayana-jta/runtime/src/main/java/io/quarkus/narayana/jta/runtime/internal/tsr/AgroalOrderedLastSynchronizationList.java:143

     * <p>
     *
     * @param synchronization The synchronization to register
     * @throws IllegalStateException if the transaction is in the wrong state:
     *         <ol>
     *         <li>the transaction has already prepared;
     *         <li>the transaction is marked rollback only
     *         <li>the group that the synchronization should belong to has already been processed
     *         </ol>
     */
    public void registerInterposedSynchronization(Synchronization synchronization) {
        int status = tsr.getTransactionStatus();

        switch (status) {
            case Status.STATUS_ACTIVE:
            case Status.STATUS_PREPARING:
                break;
            default:
                throw new IllegalStateException(REGISTER_SYNC_ERROR + status);
        }

        // add the synchronization to the group that matches this package and, if there is no such group
        // then add it to the catch-all group (otherSyncs)
        String packageName = synchronization.getClass().getName();
        SynchronizationGroup synchGroup = otherSynchs;

        for (SynchronizationGroup g : synchGroups) {
            if (g.shouldAdd(packageName)) {
                synchGroup = g;
                break;
            }
        }

        synchGroup.add(synchronization);
    }

    /**

View on GitHub (pinned to e1c734241f)

Solutions

  1. Register interposed synchronizations while the transaction is active, earlier in the business flow
  2. Check transaction status before registering and skip/degrade gracefully if the TX is terminating
  3. Avoid acquiring new TX-enlisted resources inside completion callbacks or after commit/rollback started
  4. If triggered by a driver/extension, ensure resources are closed before the transaction completes (e.g. proper request-scope cleanup)

Example fix

// before
int status = tx.getStatus();
if (status != Status.STATUS_ACTIVE) { /* still registers -> throws */ }
registerInterposedSynchronization(sync);
// after
int status = tx.getStatus();
if (status == Status.STATUS_ACTIVE || status == Status.STATUS_PREPARING) {
    registerInterposedSynchronization(sync);
}
Defensive patterns

Strategy: validation

Validate before calling

int st = transactionManager.getStatus();
boolean registrationAllowed =
    st == jakarta.transaction.Status.STATUS_ACTIVE || st == jakarta.transaction.Status.STATUS_PREPARING;

Try / catch

try { registerInterposed(sync); } catch (IllegalStateException e) {
    if (e.getMessage().startsWith("Syncs are not allowed to be registered")) { /* run cleanup directly instead */ }
}

Prevention

When it happens

Trigger: Calling registerInterposedSynchronization(synchronization) when tx.getStatus() is not ACTIVE or PREPARING — e.g. registering during commit/rollback completion, or after the transaction already terminated.

Common situations: Connection close/cleanup handlers running after the TX decided its fate; resource enlistment triggered lazily during commit; asynchronous tasks completing and registering syncs after the TX ended; reaper-rolled-back transactions still being enlisted by slow code.

Related errors


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