tursodatabase/turso · error · ProtocolError

invalid value in server response

Error message

invalid value in server response: {e}

What it means

decode_value() converts one protocol value dict into a Python value. Missing keys ('type', 'value', 'base64'), wrong JSON shapes, or a float field that cannot be converted raise KeyError, TypeError, or ValueError inside the decoder; all three are re-raised as ProtocolError('invalid value in server response: ...') with the original cause discarded (from None). This error always means the server produced a value object that violates the protocol.

Solutions

  1. Upgrade the turso_serverless client to match the server version
  2. Confirm the URL points at a real Turso/Hrana endpoint rather than something that rewrites responses
  3. Capture the raw HTTP body of the failing response and report it to maintainers
Defensive patterns

Strategy: try-catch

Try / catch

from turso_serverless.protocol import ProtocolError

try:
    rows = conn.execute(sql, params).fetchall()
except ProtocolError as e:
    if "invalid value in server response" in str(e):
        log.error("server sent a malformed value; client=%s", client_version)
        raise
    raise

Prevention

When it happens

Trigger: A server or intermediary returns a value without its 'type' or 'value' key; a float value containing a non-numeric string; a blob entry with malformed base64.

Common situations: Version skew between an upgraded server and an older client; a gateway or proxy mangling JSON bodies; a server bug emitting malformed rows for specific data.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/64db8054b3f49d75. Report an issue: GitHub.

Appendix: source

Thrown at serverless/python/turso_serverless/protocol.py:75

        if typ == "null":
            return None
        if typ == "integer":
            return int(pv["value"])
        if typ == "float":
            raw = pv["value"]
            # A null value encodes a non-finite float (section 8.2); the
            # spec says to decode it as NaN.
            if raw is None:
                return math.nan
            return float(raw)
        if typ == "text":
            return pv["value"]
        if typ == "blob":
            # The server may omit base64 padding (section 8).
            b64 = pv["base64"]
            return base64.b64decode(b64 + "=" * (-len(b64) % 4))
    except (KeyError, TypeError, ValueError) as e:
        raise ProtocolError(f"invalid value in server response: {e}") from None
    raise ProtocolError(f"unsupported value type in server response: {typ!r}")


def build_batch_step(
    sql: str,
    args: Optional[list] = None,
    named_args: Optional[list[tuple[str, Any]]] = None,
    want_rows: bool = True,
    condition: Optional[dict] = None,
) -> dict:
    """Build a batch step for a cursor request (section 7)."""
    encoded_args = [encode_value(a) for a in args] if args else []
    encoded_named = (
        [{"name": name, "value": encode_value(val)} for name, val in named_args]
        if named_args
        else []
    )
    step: dict = {

View on GitHub (pinned to 244cde92a7)