tursodatabase/turso · error · SQLException
step() returned null, which is only returned when an error o
Error message
step() returned null, which is only returned when an error occurs
What it means
TursoStatement.step() wraps the JNI call step(long); the native method returns null only on its error path, and the Java wrapper converts that null into this SQLException. It means the native engine failed while advancing the statement in a way that did not produce a TursoStepResult (the least-informative failure mode of the step machinery).
Source
Thrown at bindings/java/src/main/java/tech/turso/core/TursoStatement.java:53
public TursoResultSet getResultSet() {
return resultSet;
}
/**
* Expects a clean statement created right after prepare method is called.
*
* @return true if the ResultSet has at least one row; false otherwise.
*/
public boolean execute() throws SQLException {
resultSet.next();
return resultSet.hasLastStepReturnedRow();
}
TursoStepResult step() throws SQLException {
final TursoStepResult result = step(this.statementPointer);
if (result == null) {
throw new SQLException("step() returned null, which is only returned when an error occurs");
}
return result;
}
/**
* Because turso supports async I/O, it is possible to return a {@link TursoStepResult} with
* {@link TursoStepResult#STEP_RESULT_ID_ROW}. However, this is handled by the native side, so you
* can expect that this method will not return a {@link TursoStepResult#STEP_RESULT_ID_ROW}.
*/
@Nullable
private native TursoStepResult step(long stmtPointer) throws SQLException;
/**
* Throws formatted SQLException with error code and message.
*
* @param errorCode Error code.
* @param errorMessageBytes Error message.View on GitHub (pinned to bad083fafb)
Solutions
- Never close the TursoDB/connection/statement while its ResultSet is still stepping — finish or abandon iteration first
- Use one statement per thread; synchronize access if sharing is unavoidable
- Catch SQLException around iteration and include the SQL in your error context (TursoStatement.toString() carries it)
- Re-run the same SQL in the CLI/tursodb to see whether the query itself errors; if it does not, report the repro — null from step() is a native diagnostics gap
Example fix
// before
new Thread(() -> { try { while (rs.next()) { emit(rs); } } catch (SQLException ignored) {} }).start();
conn.close(); // races with iteration -> step() returned null
// after
try {
while (rs.next()) { emit(rs); }
} catch (SQLException e) {
throw new IllegalStateException("step failed for: " + sql, e);
} finally {
conn.close(); // close only after iteration completes Defensive patterns
Strategy: try-catch
Try / catch
try {
while (rs.next()) {
emit(rs);
}
} catch (SQLException e) {
if (e.getMessage() != null && e.getMessage().contains("step() returned null")) {
// native error path: statement likely invalidated (closed DB/statement) or
// shared across threads. Re-prepare the statement and retry once; if it
// recurs with a single-threaded, open connection, report upstream.
} else {
throw e;
}
} Prevention
- Close statements, connections, and the DB only after iteration finishes
- Keep each statement on one thread; do not share TursoStatement instances
- Attach the SQL (TursoStatement.toString()) to step failures for reproducibility
When it happens
Trigger: Stepping a statement whose native execution fails outright: the DB or statement was closed/invalidated underneath (dangling statementPointer), invalid concurrent use of one statement across threads, or native error paths that return null rather than an error result.
Common situations: Closing the database or connection while a ResultSet is mid-iteration; sharing a TursoStatement between threads without synchronization; holding result sets open across connection lifecycle events; bugs in native error mapping.
Related errors
- step() returned invalid result: " + errorMessage
- step() returned invalid result: " + lastStepResult
- Unable to load necessary native library
- The result set is not open
- ResultSet closed
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/5beebae9da84e915.
Report an issue: GitHub.