flowable/flowable-engine · error · ActivitiException
RollbackException while registering synchronization
Error message
RollbackException while registering synchronization
What it means
addTransactionListener() wraps javax.transaction.RollbackException from Transaction.registerSynchronization() in this ActivitiException. JTA throws RollbackException when the transaction has already been marked rollback-only, so no further synchronizations can be registered.
Solutions
- Find what marked the transaction rollback-only earlier in the command (the original failure log) — that is the real problem.
- Check transaction.getStatus() for STATUS_MARKED_ROLLBACK before registering listeners; skip registration when set.
- Move listener registration earlier in the command, before operations that can mark rollback-only.
- Inspect the chained RollbackException cause for the JTA provider's details.
Example fix
// before
transactionContext.addTransactionListener(TransactionState.COMMITTED, listener);
// after
if (transaction.getStatus() == Status.STATUS_ACTIVE) {
transactionContext.addTransactionListener(TransactionState.COMMITTED, listener);
} Defensive patterns
Strategy: validation
Validate before calling
boolean canRegister = transaction.getStatus() == Status.STATUS_ACTIVE; // STATUS_MARKED_ROLLBACK forbids synchronization
Try / catch
try { ctx.addTransactionListener(TransactionState.COMMITTED, listener); } catch (ActivitiException e) { if (e.getCause() instanceof RollbackException) { log.warn("transaction already marked rollback-only; listener skipped"); } else { throw e; } } Prevention
- Register listeners at the start of the command.
- Fix the earlier failure that marked the transaction rollback-only.
- Skip COMMITTED listeners when status is STATUS_MARKED_ROLLBACK.
When it happens
Trigger: Adding a transaction listener after an earlier operation in the same command marked the transaction rollback-only (failed command, business exception, setRollbackOnly).
Common situations: Engine command partially failed, then teardown or interceptor code tries to attach a COMMITTED-state listener; application code marked the transaction rollback before engine listeners were registered.
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
- RollbackException while registering synchronization
- IllegalStateException while registering synchronization
- IllegalStateException 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/36cee8137d639579.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/jta/JtaTransactionContext.java:79
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;
}
@Override
View on GitHub (pinned to d6d39ce1c6)