tursodatabase/turso · error · ProtocolError

batch response is missing the result for statement {i}

Error message

batch response is missing the result for statement {i}

What it means

After decoding a batch response, every statement must have produced either a result or a recorded error; _decode_batch_result raises ProtocolError if any decoded per-statement result is None (the statement never completed). This catches server replies that silently omit a step's outcome, leaving the batch's final state ambiguous.

Source

Thrown at serverless/python/turso_serverless/connection.py:401

        # at the errors, so a failure can still report what completed.
        results: list[BatchResult | None] = [
            Connection._decode_batch_statement_result(step_results[offset + i], sql)
            for i, (sql, _parameters) in enumerate(stmts)
        ]
        rollback_error = None
        if commit_index is not None and step_errors[commit_index + 1] is not None:
            rollback_error = _classify_error(_server_error(step_errors[commit_index + 1]))
        try:
            Connection._raise_batch_step_error(step_errors, len(stmts), offset, commit_index, results)
        except Exception as primary_error:
            if rollback_error is not None:
                primary_error.rollback_error = rollback_error
            raise
        if rollback_error is not None:
            raise rollback_error
        for i, result in enumerate(results):
            if result is None:
                raise ProtocolError(f"batch response is missing the result for statement {i}")
        return results

    @staticmethod
    def _decode_batch_statement_result(step_result: Any, sql: str) -> BatchResult | None:
        """Decode one statement result of a batch response (section 8.4),
        or None when the statement did not complete."""
        if not isinstance(step_result, dict):
            return None
        columns = [c.get("name") or "" for c in step_result.get("cols") or []]
        rows = [tuple(decode_value(v) for v in row) for row in step_result.get("rows") or []]
        if columns:
            description = tuple((name, None, None, None, None, None, None) for name in columns)
            rowcount = -1
        else:
            description = None
            rowcount = step_result.get("affected_row_count") or 0
        lastrowid = None
        raw_rowid = step_result.get("last_insert_rowid")

View on GitHub (pinned to c1e5928725)

Solutions

  1. Check step_errors for the failing statement index first — a step error may explain the missing result.
  2. Upgrade client SDK and server to matching versions to rule out protocol drift.
  3. Retry the batch; treat the database state as unknown and make statements idempotent where possible.
  4. Capture the full response and report to Turso if reproducible on current versions.

Example fix

# before
results = conn.batch(stmts)  # ProtocolError: missing result for statement 2
# after
try:
    results = conn.batch(stmts)
except ProtocolError as e:
    logger.warning("batch incomplete: %s — retrying idempotent batch", e)
    results = conn.batch(stmts)
Defensive patterns

Strategy: try-catch

Try / catch

try:
    results = conn.batch(stmts)
except ProtocolError as e:
    log.warning("batch incomplete: %s — DB state unknown, retrying idempotent batch", e)
    results = conn.batch(stmts)

Prevention

When it happens

Trigger: A batch response where one statement's decoded result decodes to None (e.g. step_result payload lacks the expected fields after a failed/incomplete step that wasn't captured in step_errors), typically after protocol drift or an interrupted server-side batch.

Common situations: Client/server version mismatch, proxy interference dropping JSON fields, and server bugs on partially failed batches where completion status isn't reported.

Related errors


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