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

  1. Log the full exception: the stringified TursoStepResult in the message carries the result id
  2. Treat it exactly like the message-bearing variant: the set is closed, re-execute on a fresh statement if needed
  3. 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

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


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)