tursodatabase/turso · error · java.sql.SQLException

Exception while binding NULL value at position {position}

Error message

Exception while binding NULL value at position {position}

What it means

bindNull(position) calls the native bind and throws when the returned result code is non-zero (SQLite-style result codes, where 0 means SQLITE_OK). Non-zero here almost always means SQLITE_RANGE: the position is outside 1..parameterCount() of the prepared SQL. Positions are 1-based, and bindNull is also the fallback for every null routed through bindObject, so the error frequently surfaces indirectly.

Source

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

    if (columnNames != null) {
      this.resultSet.setColumnNames(columnNames);
    }
  }

  @Nullable
  private native String[] columns(long statementPointer) throws SQLException;

  /**
   * Binds a NULL value to the prepared statement at the specified position.
   *
   * @param position The index of the SQL parameter to be set to NULL.
   * @return <a href="https://www.sqlite.org/c3ref/c_abort.html">Result Codes</a>
   * @throws SQLException If a database access error occurs.
   */
  public int bindNull(int position) throws SQLException {
    final int result = bindNull(statementPointer, position);
    if (result != 0) {
      throw new SQLException("Exception while binding NULL value at position " + position);
    }
    return result;
  }

  private native int bindNull(long statementPointer, int position) throws SQLException;

  /**
   * Binds an integer value to the prepared statement at the specified position. This function calls
   * bindLong because turso treats all integers as long (as well as SQLite).
   *
   * <p>According to SQLite documentation, the value is a signed integer, stored in 0, 1, 2, 3, 4,
   * 6, or 8 bytes depending on the magnitude of the value.
   *
   * @param position The index of the SQL parameter to be set.
   * @param value The integer value to bind to the parameter.
   * @return A result code indicating the success or failure of the operation.
   * @throws SQLException If a database access error occurs.
   */

View on GitHub (pinned to 244cde92a7)

Solutions

  1. Use 1-based positions: the i-th placeholder is bound with i
  2. Validate against stmt.parameterCount() before binding
  3. Keep the SQL string and its bind calls adjacent in one method so edits stay in sync

Example fix

// before
stmt.bindNull(0); // throws

// after
if (position >= 1 && position <= stmt.parameterCount()) {
  stmt.bindNull(position); // first '?' is position 1
}
Defensive patterns

Strategy: validation

Validate before calling

int paramCount = stmt.parameterCount(); // throws its own SQLException if the statement is dead
if (position < 1 || position > paramCount) {
  throw new IllegalArgumentException("bind position must be 1.." + paramCount);
}
stmt.bindNull(position);

Try / catch

try {
  stmt.bindNull(position);
} catch (SQLException e) {
  throw new IllegalArgumentException(
      "bind failed for position " + position + " (1-based, max " + safeParamCount(stmt) + ")", e);
}

Prevention

When it happens

Trigger: Calling bindNull(0) or a negative position; passing a position greater than the number of '?' placeholders in the SQL; binding on a stale statement pointer after the statement was closed.

Common situations: 0-based loop indexes from caller code fed directly as positions; SQL text edited to add or remove placeholders while the bind sequence stayed unchanged; reusing a statement after its connection closed.

Related errors


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