tursodatabase/turso · error · java.sql.SQLException

Exception while retrieving parameter count

Error message

Exception while retrieving parameter count

What it means

TursoStatement.parameterCount() calls the native parameterCount(pointer) JNI method (bindings/java/rs_src/turso_statement.rs:330) to get the number of '?' placeholders in the prepared statement. The native method returns -1 when the statement pointer is invalid (zeroed after close, or unresolvable), and the wrapper at TursoStatement.java:285 turns that -1 into this SQLException. A healthy statement always has a parameter count >= 0, so -1 means the handle was unusable, almost always a closed/freed statement.

Solutions

  1. Call parameterCount() before the statement is closed - ideally right after prepare, while binding parameters.
  2. Guard with if (!stmt.isClosed()) before calling, and skip or re-prepare when closed.
  3. Cache the count in a local variable at prepare time if you need it during cleanup or logging later.
  4. Do not cache or reuse statement objects across connection pool recycle boundaries; re-prepare instead.
  5. If it fails on a freshly prepared, open statement, report/upgrade the bindings - the count of a live statement can never legitimately be -1.

Example fix

// before
public void bindAll(TursoStatement stmt, Object[] args) {
    int n = stmt.parameterCount(); // may run after pool closed the statement
    ...
}

// after
public void bindAll(TursoStatement stmt, Object[] args) throws SQLException {
    if (stmt.isClosed()) {
        throw new SQLException("statement is closed; re-prepare before binding");
    }
    int n = stmt.parameterCount();
    ...
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (stmt != null && !stmt.isClosed()) {
    int count = stmt.parameterCount();
} else {
    // re-prepare or skip
}

Try / catch

try {
    int count = stmt.parameterCount();
} catch (SQLException e) {
    if (stmt.isClosed()) { /* re-prepare statement */ } else { throw e; }
}

Prevention

When it happens

Trigger: Calling parameterCount() after statement.close(); calling it after TursoConnection.close(); generic bind helpers that query parameterCount() on statements handed back from a pool that already closed them; reflective/framework code (Hibernate, jOOQ, Spring JDBC) that calls getParameterMetaData-like paths late in the statement lifecycle.

Common situations: Caching PreparedStatement objects and calling parameterCount() after the cache or pool invalidated them; try-with-resources scoping mistakes where the count is read after the block; helper utilities that accept a Statement and defensively ask for its parameter count without checking isClosed(); driver version changes that altered when the native pointer is zeroed.

Related errors


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

Appendix: source

Thrown at bindings/java/src/main/java/tech/turso/core/TursoStatement.java:285

    if (result == -1) {
      throw new SQLException("Exception while retrieving number of changes");
    }

    return result;
  }

  private native long changes(long statementPointer) throws SQLException;

  /**
   * Returns the number of parameters in this statement. Parameters are the `?`'s that get replaced
   * by the provided arguments.
   *
   * @throws SQLException If a database access error occurs
   */
  public int parameterCount() throws SQLException {
    final int result = parameterCount(statementPointer);
    if (result == -1) {
      throw new SQLException("Exception while retrieving parameter count");
    }

    return result;
  }

  private native int parameterCount(long statementPointer) throws SQLException;

  /** Resets this statement so it's ready for re-execution */
  public void reset() throws SQLException {
    final int result = reset(statementPointer);
    if (result == -1) {
      throw new SQLException("Exception while resetting statement");
    }
    this.resultSet = TursoResultSet.of(this);
  }

  private native int reset(long statementPointer) throws SQLException;

View on GitHub (pinned to 244cde92a7)