{"record":{"id":"09492d933a094a26","repo":"tursodatabase/turso","slug":"cannot-rollback-in-autocommit-mode","errorCode":null,"errorMessage":"Cannot rollback in autocommit mode.","messagePattern":"Cannot rollback in autocommit mode\\.","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"bindings/java/src/main/java/tech/turso/core/TursoConnection.java","lineNumber":239,"sourceCode":"      }\n\n      if (inTransaction) {\n        executeInternal(\"COMMIT\");\n        inTransaction = false;\n      }\n    }\n  }\n\n  /**\n   * Rolls back the current transaction.\n   *\n   * @throws SQLException if in auto-commit mode, closed, or database error occurs.\n   */\n  public void rollback() throws SQLException {\n    synchronized (transactionLock) {\n      checkOpen();\n      if (autoCommit) {\n        throw new SQLException(\"Cannot rollback in autocommit mode.\");\n      }\n\n      if (inTransaction) {\n        executeInternal(\"ROLLBACK\");\n        inTransaction = false;\n      }\n    }\n  }\n\n  /**\n   * Lazy transaction starter. Starts a transaction if one isn't active, auto-commit is disabled,\n   * and the statement isn't a control command.\n   */\n  private void ensureTransactionStarted(String sql) throws SQLException {\n    if (autoCommit || inTransaction) return;\n\n    // Avoid recursive start for control statements\n    String trimmed = sql.trim().toUpperCase();","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/bindings/java/src/main/java/tech/turso/core/TursoConnection.java#L221-L257","documentation":"rollback() requires autoCommit to be false; calling it while still in autocommit mode throws. As with commit(), the driver only maintains transaction state after setAutoCommit(false) — a lazy BEGIN is issued on the first non-control statement, and rollback() then issues ROLLBACK. Rolling back outside a transaction is meaningless, hence the guard.","triggerScenarios":"Calling connection.rollback() in a catch block without a prior setAutoCommit(false); exception handlers that roll back unconditionally even for connections never taken out of autocommit; code after a pool reset autoCommit to true.","commonSituations":"Generic catch-and-rollback wrappers around code that sometimes runs in autocommit; connection pools restoring autoCommit=true before the application's finally block runs; partial refactors that removed setAutoCommit(false).","solutions":["Call conn.setAutoCommit(false) before the unit of work begins","Guard the rollback: `if (!conn.getAutoCommit()) conn.rollback();` in finally/catch","Structure transactional code so rollback only runs on connections you explicitly prepared for transactions"],"exampleFix":"// before\ntry { runQueries(conn); } catch (SQLException e) { conn.rollback(); throw e; }\n\n// after\nconn.setAutoCommit(false);\ntry { runQueries(conn); conn.commit(); }\ncatch (SQLException e) { if (!conn.getAutoCommit()) conn.rollback(); throw e; }","handlingStrategy":"validation","validationCode":"// safe cleanup: rollback is a no-op unless a lazy transaction actually started\nif (!conn.getAutoCommit()) {\n  conn.rollback();\n}","typeGuard":null,"tryCatchPattern":"try {\n  runInTxn(conn);\n} catch (SQLException e) {\n  if (!conn.getAutoCommit()) conn.rollback();\n  throw e;\n}","preventionTips":["Guard every rollback in generic exception handlers with !conn.getAutoCommit()","Take connections out of autocommit explicitly at the start of each transactional unit of work","Ensure pool reset logic restores autoCommit before the application's finally block runs"],"tags":["transaction","autocommit","java","jdbc"],"backgroundTag":"rollback-in-autocommit-mode","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}