tursodatabase/turso · error · SQLException

step() returned invalid result: " + lastStepResult

Error message

step() returned invalid result: " + lastStepResult

What it means

Same invalid-step-state path as its sibling, but the native result carried no error message (null or empty), so the TursoStepResult's toString is embedded instead. It is the low-information variant: the native side reported failure without text. Debug logging ('lastStepResult: {}') is the only built-in visibility into the state.

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 bad083fafb)

Solutions

  1. Enable DEBUG logging for tech.turso.core.TursoResultSet to capture the full lastStepResult state at each step
  2. Wrap the SQL and the failing step index into your own error context so the repro is actionable
  3. Check for concurrency on the connection (other threads stepping/closing) and for interrupt() usage
  4. If reproducible, minimize the SQL and report it — a message-less invalid state is a native diagnostics gap

Example fix

// before
while (rs.next()) { emit(rs); } // 'step() returned invalid result: TursoStepResult@...'

// after
try {
  while (rs.next()) { emit(rs); }
} catch (SQLException e) {
  throw new IllegalStateException("query failed: " + sql, e); // keep SQL + result context
}
// plus run with: -Djava.util.logging.level=FINE (or SLF4J debug for tech.turso.core)
Defensive patterns

Strategy: try-catch

Try / catch

try {
  while (rs.next()) {
    emit(rs);
  }
} catch (SQLException e) {
  // no native message in this variant — capture lastStepResult via debug logs
  // and attach the SQL + step index to whatever error you rethrow
  throw new IllegalStateException("step failed for query: " + sql, e);
}

Prevention

When it happens

Trigger: A step that enters an invalid state whose TursoStepResult has a null/empty error message — native failure paths that do not populate the message field (some interrupt, busy, or internal error states).

Common situations: Intermittent failures in CI that only show 'step() returned invalid result: STEP_RESULT_ID_IO/...' with no SQL text; reproducing locally works because the failure depends on timing or concurrency.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/af65885cfda07f50. Report an issue: GitHub.