tursodatabase/turso · error · java.sql.SQLException

ResultSet closed

Error message

ResultSet closed

What it means

checkOpen() is the shared guard that most ResultSet operations call before doing work; it throws 'ResultSet closed' when the open flag is false. The flag flips on close() and on an invalid-state step inside next(). Because the class uses three different wordings for this same condition (also 'The result set is not open' and 'The resultSet is not open'), callers must not branch on exact message text.

Solutions

  1. Check isOpen() before accessors whenever the set is shared or its lifecycle is not local
  2. Make one component the sole owner of close()
  3. After any step error, discard the set instead of calling further methods on it

Example fix

// before
Object v = rs.get(1); // after rs was closed elsewhere -> 'ResultSet closed'

// after
if (!rs.isOpen()) {
  throw new IllegalStateException("result set already consumed");
}
Object v = rs.get(1);
Defensive patterns

Strategy: validation

Validate before calling

if (!rs.isOpen()) {
  return; // or throw your own domain-specific exception
}
Object value = rs.get(1);

Try / catch

try {
  return rs.get(1);
} catch (SQLException e) {
  // same closed-set condition as the other two message variants; never match on wording
  throw new IllegalStateException("result set already closed", e);
}

Prevention

When it happens

Trigger: Any accessor that routes through checkOpen() invoked after close(), or after next() auto-closed the set following an invalid step result.

Common situations: try-with-resources closing earlier than expected in nested scopes; helper utilities closing result sets they do not own; code continuing after a swallowed step error.

Related errors


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

Appendix: source

Thrown at bindings/java/src/main/java/tech/turso/core/TursoResultSet.java:140

  /** Gets the current row number (0-based, 0 means before first row). */
  public int getRow() {
    return row;
  }

  /**
   * Checks the status of the result set.
   *
   * @return true if it's ready to iterate over the result set; false otherwise.
   */
  public boolean isOpen() {
    return open;
  }

  /** @throws SQLException if not {@link #open} */
  public void checkOpen() throws SQLException {
    if (!open) {
      throw new SQLException("ResultSet closed");
    }
  }

  public void close() throws SQLException {
    this.open = false;
  }

  public Object get(String columnName) throws SQLException {
    final int columnsLength = this.columnNames.length;
    for (int i = 0; i < columnsLength; i++) {
      if (this.columnNames[i].equals(columnName)) {
        return get(i + 1);
      }
    }

    throw new SQLException("column name " + columnName + " not found");
  }

View on GitHub (pinned to 244cde92a7)