tursodatabase/turso · error · SQLException

statement is closed

Error message

statement is closed

What it means

closeOnCompletion() flags a statement to be closed automatically once its result sets are closed. JDBC4Statement refuses to set the flag on a closed statement: `if (closed) throw new SQLException("statement is closed")`. Statements also become closed when their connection is closed.

Solutions

  1. Call closeOnCompletion() immediately after creating the statement, before any execution or close path
  2. Guard the call with `if (!stmt.isClosed())`
  3. Do not cache statements across request lifecycles; let the pool manage connections only
  4. Check connection.isValid()/isClosed() first when a pool may have intervened

Example fix

// before
try (Statement stmt = conn.createStatement()) {
  ...
} // stmt closed here
stmt.closeOnCompletion(); // too late -> SQLException

// after
Statement stmt = conn.createStatement();
stmt.closeOnCompletion(); // configure right after creation
try (stmt) {
  ...
}
Defensive patterns

Strategy: validation

Validate before calling

if (!stmt.isClosed()) {
  stmt.closeOnCompletion();
}

Try / catch

try {
  stmt.closeOnCompletion();
} catch (SQLException e) {
  if (e.getMessage() != null && e.getMessage().contains("statement is closed")) {
    // already closed: nothing to configure
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling stmt.closeOnCompletion() after stmt.close(); after the owning connection was closed (connection close cascades to statements); on a statement reclaimed or evicted by a pool; configuring a statement pulled from an app-level cache that was already shut down.

Common situations: Setup logic split across methods so close() (or try-with-resources) runs before the configuration call; web handlers reusing statements cached beyond the request; pool eviction (idle timeout, invalidation on error) closing statements underneath the application.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/1c72a2d8d683f67c. Report an issue: GitHub.

Appendix: source

Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4Statement.java:432

  public boolean isClosed() throws SQLException {
    return this.closed;
  }

  @Override
  public void setPoolable(boolean poolable) throws SQLException {
    // TODO
  }

  @Override
  public boolean isPoolable() throws SQLException {
    // TODO
    return false;
  }

  @Override
  public void closeOnCompletion() throws SQLException {
    if (closed) {
      throw new SQLException("statement is closed");
    }
    closeOnCompletion = true;
  }

  /**
   * Indicates whether the statement should be closed automatically when all its dependent result
   * sets are closed.
   */
  @Override
  public boolean isCloseOnCompletion() throws SQLException {
    if (closed) {
      throw new SQLException("statement is closed");
    }
    return closeOnCompletion;
  }

  @Override
  @SkipNullableCheck

View on GitHub (pinned to 244cde92a7)