{"record":{"id":"36b8b6e0856a0149","repo":"tursodatabase/turso","slug":"exception-while-retrieving-total-number-of-changes","errorCode":null,"errorMessage":"Exception while retrieving total number of changes","messagePattern":"Exception while retrieving total number of changes","errorType":"exception","errorClass":"java.sql.SQLException","httpStatus":null,"severity":"error","filePath":"bindings/java/src/main/java/tech/turso/core/TursoStatement.java","lineNumber":252,"sourceCode":"      bindDouble(parameterIndex, (Float) x);\n    } else if (x instanceof Double) {\n      bindDouble(parameterIndex, (Double) x);\n    } else if (x instanceof byte[]) {\n      bindBlob(parameterIndex, (byte[]) x);\n    } else {\n      throw new SQLException(\"Unsupported object type in bindObject: \" + x.getClass().getName());\n    }\n  }\n\n  /**\n   * Returns total number of changes.\n   *\n   * @throws SQLException If a database access error occurs\n   */\n  public long totalChanges() throws SQLException {\n    final long result = totalChanges(statementPointer);\n    if (result == -1) {\n      throw new SQLException(\"Exception while retrieving total number of changes\");\n    }\n\n    return result;\n  }\n\n  private native long totalChanges(long statementPointer) throws SQLException;\n\n  /**\n   * Returns number of changes.\n   *\n   * @throws SQLException If a database access error occurs\n   */\n  public long changes() throws SQLException {\n    final long result = changes(statementPointer);\n    if (result == -1) {\n      throw new SQLException(\"Exception while retrieving number of changes\");\n    }\n","sourceCodeStart":234,"sourceCodeEnd":270,"githubUrl":"https://github.com/tursodatabase/turso/blob/244cde92a7df7f9b8b8b7a4075c35a12977e303e/bindings/java/src/main/java/tech/turso/core/TursoStatement.java#L234-L270","documentation":"totalChanges() calls the native totalChanges(statementPointer), which returns -1 as its error sentinel; any other value is the change count. So this throw means the native side could not read the counter — typically a stale or invalid statement pointer (statement or connection already closed) rather than a data problem. The sibling changes() method uses the same -1 sentinel convention.","triggerScenarios":"Calling totalChanges() after the statement was closed or finalized; calling it after the connection closed; invoking it from a thread while another finalizes the statement.","commonSituations":"Reading change counts in cleanup/finally blocks after the statement was already closed; caching statements across connection lifecycle events; pooled-connection races.","solutions":["Call totalChanges() on a live statement immediately after execute/update, before any close","Do not cache statement pointers across connection lifecycle events","If it still fails on a fresh statement, treat it as an engine-level error and check the DB/disk"],"exampleFix":"// before\nstmt.close();\nlong n = stmt.totalChanges(); // stale pointer -> throws\n\n// after\nlong n = stmt.totalChanges(); // read while the statement is still open\nstmt.close();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return stmt.totalChanges();\n} catch (SQLException e) {\n  // native returned the -1 sentinel: the statement is stale or the engine errored\n  throw new IllegalStateException(\"totalChanges unavailable; statement no longer usable\", e);\n}","preventionTips":["Read totalChanges() immediately after the update, before any close or reset","Never cache statement pointers across connection lifecycle events","Treat -1-style sentinel failures as stale-handle signals: re-prepare instead of retrying"],"tags":["java","native-library","statement","changes-count","stale-handle"],"backgroundTag":"native-call-failure","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"}