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
- Register transaction listeners within the same command/thread that owns the active transaction.
- Check transaction status before registering: only add listeners while the transaction is ACTIVE.
- Inspect the chained IllegalStateException for the JTA provider's specific rejection reason.
- 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
- Register listeners inside the same command/thread owning the transaction.
- Never register from async threads or afterCommit code.
- Guard registration with an ACTIVE status check.
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
- IllegalStateException while registering synchronization
- RollbackException while registering synchronization
- RollbackException while registering synchronization
- SystemException while registering synchronization
- SystemException while registering synchronization
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)