flowable/flowable-engine · error · ActivitiException

IllegalStateException while registering synchronization

Error message

IllegalStateException while registering synchronization 

What it means

addTransactionListener() registers a TransactionStateSynchronization on the current JTA transaction. An IllegalStateException from Transaction.registerSynchronization() is wrapped in this ActivitiException. Per JTA, it is thrown when the transaction is no longer active or the thread is not associated with it.

Solutions

  1. Register transaction listeners within the same command/thread that owns the active transaction.
  2. Check transaction status before registering: only add listeners while the transaction is ACTIVE.
  3. Inspect the chained IllegalStateException for the JTA provider's specific rejection reason.
  4. If listeners must survive command scope, use engine job execution or event listeners instead of JTA synchronizations.

Example fix

// before (async thread, transaction already gone)
executor.submit(() -> commandContext.getTransactionContext().addTransactionListener(state, listener));
// after (inside the command)
Context.getCommandContext().getTransactionContext().addTransactionListener(TransactionState.COMMITTED, listener);
Defensive patterns

Strategy: validation

Validate before calling

boolean canRegister = transaction.getStatus() == Status.STATUS_ACTIVE;

Try / catch

try { ctx.addTransactionListener(TransactionState.COMMITTED, listener); } catch (ActivitiException e) { if (e.getCause() instanceof IllegalStateException) { log.warn("listener skipped: transaction no longer active"); } else { throw e; } }

Prevention

When it happens

Trigger: Calling addTransactionListener (e.g. via engine transaction API) after the transaction was committed or rolled back, in a different thread than the transaction, or when the transaction is in a state that forbids new synchronizations.

Common situations: Registering listeners from async jobs or executor threads after command completion; listener registration in afterCommit hooks; transaction timed out before the listener was added.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/377332b83102e2e2. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/jta/JtaTransactionContext.java:77

        }
    }

    protected Transaction getTransaction() {
        try {
            return transactionManager.getTransaction();
        } catch (SystemException e) {
            throw new ActivitiException("SystemException while getting transaction ", e);
        }
    }

    @Override
    public void addTransactionListener(TransactionState transactionState, final TransactionListener transactionListener) {
        Transaction transaction = getTransaction();
        CommandContext commandContext = Context.getCommandContext();
        try {
            transaction.registerSynchronization(new TransactionStateSynchronization(transactionState, transactionListener, commandContext));
        } catch (IllegalStateException e) {
            throw new ActivitiException("IllegalStateException while registering synchronization ", e);
        } catch (RollbackException e) {
            throw new ActivitiException("RollbackException while registering synchronization ", e);
        } catch (SystemException e) {
            throw new ActivitiException("SystemException while registering synchronization ", e);
        }
    }

    public static class TransactionStateSynchronization implements Synchronization {

        protected final TransactionListener transactionListener;
        protected final TransactionState transactionState;
        private final CommandContext commandContext;

        public TransactionStateSynchronization(TransactionState transactionState, TransactionListener transactionListener, CommandContext commandContext) {
            this.transactionState = transactionState;
            this.transactionListener = transactionListener;
            this.commandContext = commandContext;
        }

View on GitHub (pinned to d6d39ce1c6)