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
- Use 1-based positions: the i-th placeholder is bound with i
- Validate against stmt.parameterCount() before binding
- 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
- Treat bind positions as 1-based everywhere in the codebase
- Validate positions once in a shared helper using parameterCount()
- Keep the SQL string and its bind calls in one method
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
- Exception while binding long value at position {position}
- Exception while binding double value at position {position}
- Exception while binding text value at position {position}
- Exception while binding blob value at position {position}
- Unsupported object type in bindObject: {className}
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/b58c61190b01c7cb.
Report an issue: GitHub.