tursodatabase/turso · error · ProtocolError

cursor response body ended before the cursor response line

Error message

cursor response body ended before the cursor response line

What it means

ProtocolError raised by _parse_cursor_body() (session.py:60-65) when the /v3/cursor response body contains no non-empty lines — the stream ended before the required cursor response line (protocol section 7.1). Session.execute_stmt catches it, resets the stream (baton cleared, autocommit restored), and re-raises, so the connection itself survives; the next statement opens a fresh stream.

Source

Thrown at serverless/python/turso_serverless/session.py:65

class StmtResult:
    """The decoded output of one executed statement."""

    columns: list[str]
    rows: list[tuple]
    affected_rows: int
    last_insert_rowid: Optional[int]


# Index of the autocommit probe step appended to every cursor batch.
_PROBE_STEP = 1


def _parse_cursor_body(raw: bytes) -> tuple[dict, list[dict]]:
    """Split a cursor response body (section 7.1) into the cursor response
    line and the decoded entries that follow it."""
    lines = [line for line in raw.decode("utf-8").splitlines() if line.strip()]
    if not lines:
        raise ProtocolError("cursor response body ended before the cursor response line")
    try:
        cursor_resp = json.loads(lines[0])
    except ValueError as e:
        raise ProtocolError(f"invalid cursor response: {e}") from None
    entries = []
    for line in lines[1:]:
        try:
            entries.append(json.loads(line))
        except ValueError as e:
            raise ProtocolError(f"invalid cursor entry: {e}") from None
    return cursor_resp, entries


class _CursorDecoder:
    """Folds streamed cursor entries (section 7.2) into a statement result,
    tracking the trailing autocommit probe step separately from the caller's
    step."""

View on GitHub (pinned to bad083fafb)

Solutions

  1. Point the client directly at the database HTTP endpoint and retry — the driver reset the stream, so the next statement starts clean
  2. Fix or remove the proxy that produced the empty body
  3. If it reproduces against the real endpoint, upgrade client/server and report with the request body that triggered it
Defensive patterns

Strategy: retry

Try / catch

from turso_serverless.protocol import ProtocolError

for attempt in range(2):
    try:
        rows = conn.execute(sql).fetchall()
        break
    except ProtocolError as e:
        if "ended before the cursor response line" not in str(e) or attempt:
            raise
        # driver reset the stream; next statement starts a new one

Prevention

When it happens

Trigger: The server returns HTTP 200 with an empty or whitespace-only body for a cursor batch; an intermediary (LB, API gateway) substitutes an empty body; a server bug drops the body while keeping the status.

Common situations: Misconfigured reverse proxy or load balancer in front of the database endpoint; serverless platforms stripping streaming responses; transient server faults during deploys.

Related errors


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