{"record":{"id":"bbcd299e02b274dd","repo":"hibernate/hibernate-orm","slug":"unable-to-mark-transaction-for-rollback-only","errorCode":null,"errorMessage":"Unable to mark transaction for rollback only","messagePattern":"Unable to mark transaction for rollback only","errorType":"exception","errorClass":"TransactionException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionAdapterUserTransactionImpl.java","lineNumber":107,"sourceCode":"\t\ttry {\n\t\t\tfinal var status = StatusTranslator.translate( userTransaction.getStatus() );\n\t\t\tif ( status == null ) {\n\t\t\t\tthrow new TransactionException( \"UserTransaction reported transaction status as unknown\" );\n\t\t\t}\n\t\t\treturn status;\n\t\t}\n\t\tcatch (SystemException e) {\n\t\t\tthrow new TransactionException( \"JTA UserTransaction.getStatus() failed\", e );\n\t\t}\n\t}\n\n\t@Override\n\tpublic void markRollbackOnly(){\n\t\ttry {\n\t\t\tuserTransaction.setRollbackOnly();\n\t\t}\n\t\tcatch (SystemException e) {\n\t\t\tthrow new TransactionException( \"Unable to mark transaction for rollback only\", e );\n\t\t}\n\t}\n\n\t@Override\n\tpublic void setTimeOut(int seconds) {\n\t\tif ( seconds > 0 ) {\n\t\t\ttry {\n\t\t\t\tuserTransaction.setTransactionTimeout( seconds );\n\t\t\t}\n\t\t\tcatch (SystemException e) {\n\t\t\t\tthrow new TransactionException( \"Unable to apply requested transaction timeout\", e );\n\t\t\t}\n\t\t}\n\t}\n}\n","sourceCodeStart":89,"sourceCodeEnd":123,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionAdapterUserTransactionImpl.java#L89-L123","documentation":"markRollbackOnly() on the UT adapter calls userTransaction.setRollbackOnly(); a SystemException is wrapped as 'Unable to mark transaction for rollback only'. Reached via Transaction.markRollbackOnly() or as rollback()'s fallback when Hibernate was not the initiator. The transaction may remain active afterwards - it must be resolved by explicit rollback, timeout, or container intervention.","triggerScenarios":"markRollbackOnly(), or the rollback() fallback, while userTransaction.setRollbackOnly() throws SystemException - TM internal error, no active UT transaction on the thread, or shutdown race.","commonSituations":"Marking rollback-only outside an active UT transaction; TM internal failure; operations during TM shutdown.","solutions":["Check UserTransaction.getStatus() before marking rollback-only","Inspect the SystemException cause in the TM logs","If you initiated the transaction, call rollback() instead of markRollbackOnly()","Restart an unhealthy TM"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Mark rollback-only only inside an active UT transaction\nint st = ut.getStatus();\nif ( st == jakarta.transaction.Status.STATUS_ACTIVE\n        || st == jakarta.transaction.Status.STATUS_MARKED_ROLLBACK ) {\n    ut.setRollbackOnly();\n}","typeGuard":null,"tryCatchPattern":"try {\n    session.getTransaction().markRollbackOnly();\n}\ncatch (TransactionException e) {\n    log.error(\"could not mark rollback-only; attempting full rollback\", e);\n    session.getTransaction().rollback();\n}","preventionTips":["Check UT status before marking rollback-only","Use rollback() when your code owns the transaction","Investigate recurring TM SystemExceptions at the environment level"],"tags":["jta","transactions","hibernate","usertransaction","rollback-only"],"backgroundTag":"mark-rollback-only-failed","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}