tursodatabase/turso · error · SQLException

database connection closed

Error message

database connection closed

What it means

SQLException thrown by TursoConnection.checkOpen() when a JDBC method is invoked on a connection whose close() has already completed (the closed flag is set). Every state-touching method — prepare, commit, rollback, setAutoCommit, getAutoCommit, setTransactionIsolation, getTransactionIsolation — calls checkOpen() first, matching standard JDBC 'connection closed' behavior.

Source

Thrown at bindings/java/src/main/java/tech/turso/core/TursoConnection.java:64

   * Creates a connection using an existing TursoDB instance. This is useful for encrypted databases
   * created with TursoDB.createWithEncryption().
   *
   * @param url e.g. "jdbc:turso:fileName"
   * @param database an existing TursoDB instance
   */
  public TursoConnection(String url, TursoDB database) throws SQLException {
    this.url = url;
    this.database = database;
    this.connectionPtr = this.database.connect();
  }

  private static TursoDB open(String url, String filePath, Properties properties)
      throws SQLException {
    return TursoDB.create(url, filePath);
  }

  public void checkOpen() throws SQLException {
    if (isClosed()) throw new SQLException("database connection closed");
  }

  public String getUrl() {
    return url;
  }

  public void close() throws SQLException {
    if (isClosed()) {
      return;
    }

    // Roll back any pending transaction before closing
    synchronized (transactionLock) {
      if (inTransaction) {
        try {
          executeInternal("ROLLBACK");
        } catch (SQLException e) {
          // Log but don't throw - we're closing anyway

View on GitHub (pinned to bad083fafb)

Solutions

  1. Check conn.isClosed() before use and reopen or re-borrow from the pool when true
  2. Scope connections with try-with-resources so close() happens after the last use
  3. Fix pool configuration (test-on-borrow, sane eviction) so connections are not closed underneath active users

Example fix

// before
try (Connection conn = DriverManager.getConnection(url)) {
  doWork(conn);
}
conn.prepareStatement(sql); // SQLException: database connection closed

// after
try (Connection conn = DriverManager.getConnection(url)) {
  doWork(conn);
  conn.prepareStatement(sql); // still inside the resource scope
}
Defensive patterns

Strategy: validation

Validate before calling

if (conn.isClosed()) {
  conn = DriverManager.getConnection(url, props); // or re-borrow from the pool
}
try (var ps = conn.prepareStatement(sql)) {
  ps.execute();
}

Type guard

static boolean isUsable(java.sql.Connection c) throws SQLException {
  return c != null && !c.isClosed();
}

Try / catch

try {
  doWork(conn);
} catch (SQLException e) {
  if ("database connection closed".equals(e.getMessage())) {
    conn = DriverManager.getConnection(url, props); // reopen / re-borrow
    doWork(conn);
  } else throw e;
}

Prevention

When it happens

Trigger: conn.prepareStatement(...) after conn.close(); using a connection outside its try-with-resources block; a connection pool handing out a connection that another thread or an eviction policy already closed.

Common situations: Cleanup code closing early while a background job still uses the connection; pool eviction racing active borrowers; test fixtures closing a shared connection in @AfterAll while later suites reuse it; wrappers that close on error paths but continue processing.

Understand the failure class

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/708bbfc8867597cc. Report an issue: GitHub.