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
- Call parameterCount() before the statement is closed - ideally right after prepare, while binding parameters.
- Guard with if (!stmt.isClosed()) before calling, and skip or re-prepare when closed.
- Cache the count in a local variable at prepare time if you need it during cleanup or logging later.
- Do not cache or reuse statement objects across connection pool recycle boundaries; re-prepare instead.
- 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
- Query parameterCount() right after prepare, before any close path can run.
- Store the count locally if needed during later cleanup or logging.
- Validate statements coming from pools with isClosed() before use.
- Avoid reflective late-bound calls against statement objects you do not own.
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
- Exception while resetting statement
- Exception while retrieving number of changes
- turso does not support stored procedures
- createBlob not supported
- createClob not supported
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)