flowable/flowable-engine · error · org.flowable.common.engine.api.FlowableException

SystemException while marking transaction rollback only

Error message

SystemException while marking transaction rollback only

What it means

Flowable's JTA transaction context wraps Transaction.setRollbackOnly() and translates javax.transaction.SystemException into this FlowableException. SystemException indicates the transaction manager itself failed (a low-level, unexpected error in the JTA implementation) while trying to mark the transaction rollback-only.

Solutions

  1. Inspect the wrapped cause (getCause()) for the real transaction-manager failure and fix that underlying problem first.
  2. Verify the TransactionManager configuration: the JtaTransactionContextFactory must be wired to the app server's TransactionManager (correct JNDI name or managed bean).
  3. Upgrade/patch the application server or JTA implementation if the SystemException is a known TM bug.
  4. Check resource managers (datasources, JMS) for failures that propagate as SystemException during rollback.
  5. Retry the command in a fresh transaction; if the TM is degraded, restarting the server/container may be required.

Example fix

// before: custom TransactionManager lookup returning wrong instance
TransactionManager tm = (TransactionManager) ctx.lookup("java:comp/TransactionManager");
// after: use the container-provided manager appropriate to the server
TransactionManager tm = (TransactionManager) ctx.lookup("java:jboss/TransactionManager");
Defensive patterns

Strategy: try-catch

Validate before calling

try { tm.getTransaction(); } catch (SystemException e) {
  throw new IllegalStateException("Transaction manager unhealthy: " + e.getMessage(), e);
}

Try / catch

try {
  runtimeService.startProcessInstanceByKey("p");
} catch (FlowableException e) {
  if (e.getCause() instanceof SystemException) {
    log.error("JTA TM failure", e.getCause());
    // alert/retry; possibly restart TM
  } else throw e;
}

Prevention

When it happens

Trigger: Calling rollback() when the underlying TransactionManager/Transaction implementation throws SystemException from setRollbackOnly() — e.g. TM internals failure, corrupted transaction state, or resource manager errors surfaced as SystemException.

Common situations: Application server transaction service failures; misconfigured TransactionManager (wrong JNDI lookup / multiple TM instances); known bugs in older app server JTA implementations; resource adapter failures propagating SystemException.

Related errors


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

Appendix: source

Thrown at modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/cfg/jta/JtaTransactionContext.java:58

    @Override
    public void commit() {
        // managed transaction, ignore
    }

    @Override
    public void rollback() {
        // managed transaction, mark rollback-only if not done so already.
        try {
            Transaction transaction = getTransaction();
            int status = transaction.getStatus();
            if (status != Status.STATUS_NO_TRANSACTION && status != Status.STATUS_ROLLEDBACK) {
                transaction.setRollbackOnly();
            }
        } catch (IllegalStateException e) {
            throw new FlowableException("Unexpected IllegalStateException while marking transaction rollback only", e);
        } catch (SystemException e) {
            throw new FlowableException("SystemException while marking transaction rollback only", e);
        }
    }

    protected Transaction getTransaction() {
        try {
            return transactionManager.getTransaction();
        } catch (SystemException e) {
            throw new FlowableException("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));

View on GitHub (pinned to d6d39ce1c6)