{"record":{"id":"e2d1bd3214e763d8","repo":"tursodatabase/turso","slug":"failed-to-rollback-transaction-during-close","errorCode":null,"errorMessage":"Failed to rollback transaction during close","messagePattern":"Failed to rollback transaction during close","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"bindings/java/src/main/java/tech/turso/core/TursoConnection.java","lineNumber":83,"sourceCode":"  }\n\n  public String getUrl() {\n    return url;\n  }\n\n  public void close() throws SQLException {\n    if (isClosed()) {\n      return;\n    }\n\n    // Roll back any pending transaction before closing\n    synchronized (transactionLock) {\n      if (inTransaction) {\n        try {\n          executeInternal(\"ROLLBACK\");\n        } catch (SQLException e) {\n          // Log but don't throw - we're closing anyway\n          logger.warn(\"Failed to rollback transaction during close\", e);\n        } finally {\n          inTransaction = false;\n        }\n      }\n    }\n\n    this._close(this.connectionPtr);\n    this.closed = true;\n  }\n\n  private native void _close(long connectionPtr);\n\n  private native boolean _getAutoCommit(long connectionPtr);\n\n  public boolean isClosed() throws SQLException {\n    return closed;\n  }\n","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/bindings/java/src/main/java/tech/turso/core/TursoConnection.java#L65-L101","documentation":"TursoConnection.close() (bindings/java .../TursoConnection.java:83) checks isClosed(), then under transactionLock rolls back any open transaction via execute_internal(\"ROLLBACK\") before closing. If that ROLLBACK throws, the SQLException is caught and logged at WARN level as \"Failed to rollback transaction during close\" through tech.turso.utils.Logger; inTransaction is cleared and the native connection is still closed via _close(connectionPtr). So this is a logged symptom, not a thrown error: the pending transaction could not be unwound, usually because the underlying engine already failed (I/O error, missing/unwritable file, poisoned connection).","triggerScenarios":"Calling close() while a transaction is open (autoCommit=false with uncommitted writes) and the ROLLBACK itself fails - disk full, WAL on removed network storage, a prior fatal native error that already invalidated the connection, or close racing concurrent statement execution on the same connection.","commonSituations":"Application or connection-pool shutdown with uncommitted transactions, database files on removable/network mounts, disk exhaustion, code that swallows an earlier SQLException and closes anyway.","solutions":["Always commit or rollback explicitly in application finally blocks; treat close() as a last resort, not the transaction unwind.","Read the WARN log's attached exception - it carries the SQLException with the underlying cause (I/O error, lock, disk full); fix that root cause.","Check whether the connection was already poisoned by an earlier error; if so, the rollback failure is a symptom and the earlier failure is the real bug.","Upgrade the Java bindings; close-time rollback handling may be improved in newer releases."],"exampleFix":"// before: relying on close() to unwind the transaction\ntry (Connection conn = driver.connect(url, props)) {\n    conn.setAutoCommit(false);\n    stmt.executeUpdate(\"UPDATE t SET x = 1\");\n} // close() attempts rollback, logs WARN on failure\n\n// after: explicit rollback before close\nConnection conn = driver.connect(url, props);\ntry {\n    conn.setAutoCommit(false);\n    stmt.executeUpdate(\"UPDATE t SET x = 1\");\n    conn.commit();\n} catch (SQLException e) {\n    try { conn.rollback(); } catch (SQLException re) { log.warn(\"rollback failed\", re); }\n    throw e;\n} finally {\n    conn.close();\n}","handlingStrategy":"try-catch","validationCode":"// Before closing: end the transaction explicitly so close() never needs to roll back.\nif (!conn.isClosed() && !conn.getAutoCommit()) {\n    conn.rollback(); // or commit() - decide deliberately, not at close time\n}\nconn.close();","typeGuard":null,"tryCatchPattern":"finally {\n    if (conn != null) {\n        try {\n            if (!conn.getAutoCommit()) conn.rollback();\n        } catch (SQLException rollbackEx) {\n            log.warn(\"rollback before close failed\", rollbackEx); // capture the cause here\n        } finally {\n            try { conn.close(); } catch (SQLException closeEx) {\n                log.warn(\"close failed\", closeEx);\n            }\n        }\n    }\n}","preventionTips":["Always commit or rollback explicitly in application code; never delegate transaction cleanup to close().","Treat any earlier SQLException on a connection as potentially poisoning it; validate with a trivial query before further use.","Keep database files on reliable storage; verify disk space and mounts before shutdown sequences.","Configure pools to reset transaction state on check-in (e.g. rollbackOnReturn) so pooled connections are never handed back mid-transaction."],"tags":["java","jdbc","connection-close","rollback","transaction","logging","turso"],"backgroundTag":"transaction-rollback-failed","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}