{"record":{"id":"36cee8137d639579","repo":"flowable/flowable-engine","slug":"rollbackexception-while-registering-synchronizatio-36cee8","errorCode":null,"errorMessage":"RollbackException while registering synchronization ","messagePattern":"RollbackException while registering synchronization ","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/jta/JtaTransactionContext.java","lineNumber":79,"sourceCode":"\r\n    protected Transaction getTransaction() {\r\n        try {\r\n            return transactionManager.getTransaction();\r\n        } catch (SystemException e) {\r\n            throw new ActivitiException(\"SystemException while getting transaction \", e);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void addTransactionListener(TransactionState transactionState, final TransactionListener transactionListener) {\r\n        Transaction transaction = getTransaction();\r\n        CommandContext commandContext = Context.getCommandContext();\r\n        try {\r\n            transaction.registerSynchronization(new TransactionStateSynchronization(transactionState, transactionListener, commandContext));\r\n        } catch (IllegalStateException e) {\r\n            throw new ActivitiException(\"IllegalStateException while registering synchronization \", e);\r\n        } catch (RollbackException e) {\r\n            throw new ActivitiException(\"RollbackException while registering synchronization \", e);\r\n        } catch (SystemException e) {\r\n            throw new ActivitiException(\"SystemException while registering synchronization \", e);\r\n        }\r\n    }\r\n\r\n    public static class TransactionStateSynchronization implements Synchronization {\r\n\r\n        protected final TransactionListener transactionListener;\r\n        protected final TransactionState transactionState;\r\n        private final CommandContext commandContext;\r\n\r\n        public TransactionStateSynchronization(TransactionState transactionState, TransactionListener transactionListener, CommandContext commandContext) {\r\n            this.transactionState = transactionState;\r\n            this.transactionListener = transactionListener;\r\n            this.commandContext = commandContext;\r\n        }\r\n\r\n        @Override\r","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/jta/JtaTransactionContext.java#L61-L97","documentation":"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.","triggerScenarios":"Adding a transaction listener after an earlier operation in the same command marked the transaction rollback-only (failed command, business exception, setRollbackOnly).","commonSituations":"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.","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."],"exampleFix":"// before\ntransactionContext.addTransactionListener(TransactionState.COMMITTED, listener);\n// after\nif (transaction.getStatus() == Status.STATUS_ACTIVE) {\n    transactionContext.addTransactionListener(TransactionState.COMMITTED, listener);\n}","handlingStrategy":"validation","validationCode":"boolean canRegister = transaction.getStatus() == Status.STATUS_ACTIVE; // STATUS_MARKED_ROLLBACK forbids synchronization","typeGuard":null,"tryCatchPattern":"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; } }","preventionTips":["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."],"tags":["jta","transaction","rollback","synchronization"],"backgroundTag":"invalid-state-transition","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}