tursodatabase/turso · error · DatabaseError

batch response is missing statement results

Error message

batch response is missing statement results

What it means

After decoding the batch response, batch() maps one result per user statement. If any statement slot is still null (no step result found for it), the batch did not fully execute, and returning partial data silently would hide lost statements — so it throws.

Source

Thrown at serverless/javascript/src/session.ts:682

      const stepError = stepErrors[firstUserStepIdx + i];
      if (stepError) {
        throwStepError(stepError, i);
      }
    }
    if (commitIdx >= 0 && stepErrors[commitIdx]) {
      throwStepError(stepErrors[commitIdx]);
    }
    if (rollbackError) {
      const error = new DatabaseError(
        rollbackError.message || 'Batch rollback failed',
        rollbackError.code,
      );
      error.batchResults = results;
      throw error;
    }

    if (results.some(result => result === null)) {
      throw new DatabaseError('batch response is missing statement results');
    }
    return results;
  }

  /** Decode one statement result of a batch response (section 8.4) into
   * the per-statement result shape returned by `batch()`. */
  private decodeBatchStepResult(stepResult: ExecuteResult, safeIntegers: boolean, raw: boolean): any {
    const columns = (stepResult.cols ?? []).map(col => col.name ?? '');
    const columnTypes = (stepResult.cols ?? []).map(col => col.decltype || '');
    const rows = (stepResult.rows ?? []).map(row => {
      const decoded = row.map(value => decodeValue(value, safeIntegers));
      return raw ? decoded : this.createObjectRow(decoded, columns);
    });
    let lastInsertRowid: number | undefined;
    if (stepResult.last_insert_rowid !== undefined && stepResult.last_insert_rowid !== null) {
      lastInsertRowid = typeof stepResult.last_insert_rowid === 'number'
        ? stepResult.last_insert_rowid
        : parseInt(stepResult.last_insert_rowid, 10);

View on GitHub (pinned to c1e5928725)

Solutions

  1. Check the thrown error's batchResults property to see which statement failed and why.
  2. Fix the failing statement (syntax, constraint, type error) that aborted the batch.
  3. Use batch(..., 'true') (go: true) if you want independent execution semantics where appropriate.
  4. Wrap each risky statement with SAVEPOINTs or run them in separate batch calls if partial success is acceptable.

Example fix

// before
await session.batch([
  { sql: "INSERT INTO a ..." },
  { sql: "BROKEN SQL" },
  { sql: "INSERT INTO b ..." } // never runs -> null result
]);
// after
await session.batch([
  { sql: "INSERT INTO a ..." },
  { sql: "FIXED SQL" },
  { sql: "INSERT INTO b ..." }
]);
Defensive patterns

Strategy: try-catch

Validate before calling

null

Try / catch

try { await session.batch(stmts); } catch (e) { if (e instanceof DatabaseError && e.message === 'batch response is missing statement results') { const failed = e.batchResults?.filter(r => r?.error); /* inspect failing statement and fix SQL */ } else throw e; }

Prevention

When it happens

Trigger: session.batch() where stepResults contains null (or a non-completing marker) for one of the user statements — typically because an earlier step errored and the server skipped subsequent steps, or a step result was missing.

Common situations: A mid-batch SQL error aborting the remaining statements, using batch without go:true so statements run sequentially and one failure stops the rest, protocol truncation.

Related errors


AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-31). Data as JSON: /api/errors/9f2e64f39ff006e3. Report an issue: GitHub.