{"record":{"id":"d6c418e9da17efec","repo":"tursodatabase/turso","slug":"exception-while-retrieving-parameter-count","errorCode":null,"errorMessage":"Exception while retrieving parameter count","messagePattern":"Exception while retrieving parameter count","errorType":"exception","errorClass":"java.sql.SQLException","httpStatus":null,"severity":"error","filePath":"bindings/java/src/main/java/tech/turso/core/TursoStatement.java","lineNumber":285,"sourceCode":"    if (result == -1) {\n      throw new SQLException(\"Exception while retrieving number of changes\");\n    }\n\n    return result;\n  }\n\n  private native long changes(long statementPointer) throws SQLException;\n\n  /**\n   * Returns the number of parameters in this statement. Parameters are the `?`'s that get replaced\n   * by the provided arguments.\n   *\n   * @throws SQLException If a database access error occurs\n   */\n  public int parameterCount() throws SQLException {\n    final int result = parameterCount(statementPointer);\n    if (result == -1) {\n      throw new SQLException(\"Exception while retrieving parameter count\");\n    }\n\n    return result;\n  }\n\n  private native int parameterCount(long statementPointer) throws SQLException;\n\n  /** Resets this statement so it's ready for re-execution */\n  public void reset() throws SQLException {\n    final int result = reset(statementPointer);\n    if (result == -1) {\n      throw new SQLException(\"Exception while resetting statement\");\n    }\n    this.resultSet = TursoResultSet.of(this);\n  }\n\n  private native int reset(long statementPointer) throws SQLException;\n","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/bindings/java/src/main/java/tech/turso/core/TursoStatement.java#L267-L303","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npublic void bindAll(TursoStatement stmt, Object[] args) {\n    int n = stmt.parameterCount(); // may run after pool closed the statement\n    ...\n}\n\n// after\npublic void bindAll(TursoStatement stmt, Object[] args) throws SQLException {\n    if (stmt.isClosed()) {\n        throw new SQLException(\"statement is closed; re-prepare before binding\");\n    }\n    int n = stmt.parameterCount();\n    ...\n}","handlingStrategy":"try-catch","validationCode":"if (stmt != null && !stmt.isClosed()) {\n    int count = stmt.parameterCount();\n} else {\n    // re-prepare or skip\n}","typeGuard":null,"tryCatchPattern":"try {\n    int count = stmt.parameterCount();\n} catch (SQLException e) {\n    if (stmt.isClosed()) { /* re-prepare statement */ } else { throw e; }\n}","preventionTips":["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."],"tags":["java","jdbc","turso","sqlite","statement-lifecycle","jni"],"backgroundTag":"closed-statement-handle","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}