{"record":{"id":"377332b83102e2e2","repo":"flowable/flowable-engine","slug":"illegalstateexception-while-registering-synchroniz-377332","errorCode":null,"errorMessage":"IllegalStateException while registering synchronization ","messagePattern":"IllegalStateException 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":77,"sourceCode":"        }\r\n    }\r\n\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","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cfg/jta/JtaTransactionContext.java#L59-L95","documentation":"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.","triggerScenarios":"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.","commonSituations":"Registering listeners from async jobs or executor threads after command completion; listener registration in afterCommit hooks; transaction timed out before the listener was added.","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."],"exampleFix":"// before (async thread, transaction already gone)\nexecutor.submit(() -> commandContext.getTransactionContext().addTransactionListener(state, listener));\n// after (inside the command)\nContext.getCommandContext().getTransactionContext().addTransactionListener(TransactionState.COMMITTED, listener);","handlingStrategy":"validation","validationCode":"boolean canRegister = transaction.getStatus() == Status.STATUS_ACTIVE;","typeGuard":null,"tryCatchPattern":"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; } }","preventionTips":["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."],"tags":["jta","transaction","synchronization","illegal-state"],"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"}