{"record":{"id":"84cfde5cbfbd3018","repo":"hibernate/hibernate-orm","slug":"could-not-set-transaction-to-rollback-only","errorCode":null,"errorMessage":"Could not set transaction to rollback only","messagePattern":"Could not set transaction to rollback only","errorType":"exception","errorClass":"TransactionException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionAdapterTransactionManagerImpl.java","lineNumber":106,"sourceCode":"\t\ttry {\n\t\t\tfinal var status = StatusTranslator.translate( transactionManager.getStatus() );\n\t\t\tif ( status == null ) {\n\t\t\t\tthrow new TransactionException( \"TransactionManager 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 TransactionManager#getStatus failed\", e );\n\t\t}\n\t}\n\n\t@Override\n\tpublic void markRollbackOnly() {\n\t\ttry {\n\t\t\ttransactionManager.setRollbackOnly();\n\t\t}\n\t\tcatch (SystemException e) {\n\t\t\tthrow new TransactionException( \"Could not set transaction to 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\ttransactionManager.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":88,"sourceCodeEnd":122,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/resource/transaction/backend/jta/internal/JtaTransactionAdapterTransactionManagerImpl.java#L88-L122","documentation":"markRollbackOnly() calls TransactionManager.setRollbackOnly(); a SystemException from the TM is wrapped as 'Could not set transaction to rollback only'. This is the safe path failing after a business error - the transaction may still be active and must be resolved by explicit rollback, timeout, or container intervention. Reached via Transaction.markRollbackOnly(), and via rollback() when Hibernate was not the initiator (which falls back to marking rollback-only).","triggerScenarios":"session.getTransaction().markRollbackOnly(), or the rollback() fallback on a non-initiated transaction, while TransactionManager.setRollbackOnly() throws SystemException - TM internal error, TM shutting down, or no usable transaction on the thread.","commonSituations":"TM internal failure; marking rollback-only with no active JTA transaction; races during application shutdown.","solutions":["Check TransactionManager.getStatus() before marking rollback-only","Inspect the SystemException cause in the TM logs","If Hibernate initiated the transaction, call rollback() directly instead of markRollbackOnly()","Restart the TM if it is unhealthy"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Mark rollback-only only inside an active transaction\nif ( session.getTransaction().getStatus() == TransactionStatus.ACTIVE\n        || session.getTransaction().getStatus() == TransactionStatus.MARKED_ROLLBACK ) {\n    session.getTransaction().markRollbackOnly();\n}","typeGuard":null,"tryCatchPattern":"try {\n    tx.markRollbackOnly();\n}\ncatch (TransactionException e) {\n    log.error(\"could not mark rollback-only; attempting full rollback instead\", e);\n    tx.rollback(); // may succeed where setRollbackOnly failed\n}","preventionTips":["Check transaction status before calling markRollbackOnly()","Prefer rollback() when your code initiated the transaction","Treat repeated TM SystemExceptions as an environment incident"],"tags":["jta","transactions","hibernate","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"}