tursodatabase/turso · error · java.sql.SQLException
step() returned invalid result
Error message
step() returned invalid result: {lastStepResult} What it means
The fallback branch of the same invalid-state check as its sibling error: next() received a TursoStepResult in an invalid state whose getErrorMessage() is null or empty, so the exception stringifies the raw result object instead of an engine message. Semantics are identical — the engine failed during step and the set is now closed — but the missing message makes diagnosis harder, which usually points to a binding/JNI mapping gap rather than a clean engine error.
Solutions
- Log the full exception: the stringified TursoStepResult in the message carries the result id
- Treat it exactly like the message-bearing variant: the set is closed, re-execute on a fresh statement if needed
- If it recurs deterministically, verify the Java binding and native library come from the same release; mixed old/new artifacts produce unmapped result codes
Example fix
// before
while (rs.next()) { /* rows */ } // 'step() returned invalid result: TursoStepResult@...'
// after
try {
while (rs.next()) { /* rows */ }
} catch (SQLException e) {
// no engine message available; keep the stringified result for diagnosis
log.error("invalid step result without message: {}", e.getMessage(), e);
} Defensive patterns
Strategy: try-catch
Try / catch
try {
while (rs.next()) { /* ... */ }
} catch (SQLException e) {
// no engine message available; the stringified result in the message carries the result id
log.error("step returned invalid result without message: {}", e.getMessage(), e);
} Prevention
- Keep the Java binding and native library versions in lockstep from the same release artifact
- Log full exceptions (not just messages) so result ids survive into logs
- After this error, treat the result set as closed and re-execute rather than retry the step
When it happens
Trigger: The same engine-level failures as the message-bearing variant (I/O error, corruption, bad statement state) but on a code path where the native side produced no error text, e.g. an unmapped result code in the JNI layer.
Common situations: Version skew between the Java binding and the native library after a partial upgrade; engine error states added before their Java message mapping.
Related errors
- step() returned invalid result
- column name not found
- columnIndex out of bound
- ResultSet closed
- ResultSet is not open
AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20).
Data as JSON: /api/errors/b07ea997665423cf.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/java/src/main/java/tech/turso/core/TursoResultSet.java:97
}
if (maxRows != 0 && row == maxRows) {
return false;
}
lastStepResult = this.statement.step();
log.debug("lastStepResult: {}", lastStepResult);
if (lastStepResult.isRow()) {
row++;
}
if (lastStepResult.isInInvalidState()) {
open = false;
String errorMessage = lastStepResult.getErrorMessage();
if (errorMessage != null && !errorMessage.isEmpty()) {
throw new SQLException("step() returned invalid result: " + errorMessage);
} else {
throw new SQLException("step() returned invalid result: " + lastStepResult);
}
}
pastLastRow = lastStepResult.isDone();
if (pastLastRow && row == 0) {
isEmptyResultSet = true;
}
return !pastLastRow;
}
/** Checks whether the last step result has returned row result. */
public boolean hasLastStepReturnedRow() {
return lastStepResult != null && lastStepResult.isRow();
}
/** Checks whether the cursor is positioned after the last row. */
public boolean isPastLastRow() {
return pastLastRow;View on GitHub (pinned to 244cde92a7)