tursodatabase/turso · error · TypeError

Unsupported value type: {type(value).__name__}

Error message

Unsupported value type: {type(value).__name__}

What it means

TypeError raised by encode_value() (protocol.py:30-50) when a bound parameter's type is not one of None, bool, int, float, str, bytes, or bytearray — the only types the wire protocol (section 8) can carry. There is no adapter/registration hook like sqlite3's register_adapter, so conversion is entirely the caller's job. bool is encoded as integer 0/1, blobs as base64.

Source

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

    if value is None:
        return {"type": "null"}
    if isinstance(value, bool):
        return {"type": "integer", "value": str(int(value))}
    if isinstance(value, int):
        return {"type": "integer", "value": str(value)}
    if isinstance(value, float):
        # The protocol forbids sending non-finite floats (section 8.2).
        if math.isnan(value):
            # SQLite binds NaN as NULL; JSON cannot carry it.
            return {"type": "null"}
        if math.isinf(value):
            raise ValueError("infinite float values cannot be sent over the protocol")
        return {"type": "float", "value": value}
    if isinstance(value, str):
        return {"type": "text", "value": value}
    if isinstance(value, (bytes, bytearray)):
        return {"type": "blob", "base64": base64.b64encode(value).decode("ascii")}
    raise TypeError(f"Unsupported value type: {type(value).__name__}")


def decode_value(pv: dict) -> Any:
    """Decode a protocol value (section 8) to a Python value."""
    try:
        typ = pv["type"]
        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":

View on GitHub (pinned to bad083fafb)

Solutions

  1. Convert before binding: datetime -> isoformat string, Decimal -> str or float, UUID -> str, JSON objects -> json.dumps(...)
  2. Cast numpy scalars with int()/float() and arrays with .tolist() at the row boundary
  3. Wrap rows in a single normalize(params) function used by every insert path

Example fix

// before
cur.execute("INSERT INTO events(ts, payload) VALUES (?, ?)", (event_dt, payload_dict))

// after
cur.execute("INSERT INTO events(ts, payload) VALUES (?, ?)", (event_dt.isoformat(), json.dumps(payload_dict)))
Defensive patterns

Strategy: type-guard

Validate before calling

import datetime as dt, decimal, uuid


def adapt(v):
    """Convert common Python types to the protocol's supported set."""
    if isinstance(v, (dt.datetime, dt.date)):
        return v.isoformat()
    if isinstance(v, decimal.Decimal):
        return str(v)
    if isinstance(v, uuid.UUID):
        return str(v)
    if isinstance(v, (list, dict)):
        return json.dumps(v)
    if type(v).__module__ == "numpy":
        return v.item()
    return v


params = tuple(adapt(p) for p in params)

Type guard

def is_supported_value(v) -> bool:
    """Only these types cross the wire (bool/int overlap is fine)."""
    return v is None or isinstance(v, (bool, int, float, str, bytes, bytearray))

Try / catch

try:
    cur.execute(sql, params)
except TypeError as e:
    if not str(e).startswith("Unsupported value type"):
        raise
    params = tuple(adapt(p) for p in params)  # adapt() as in validationCode
    cur.execute(sql, params)

Prevention

When it happens

Trigger: Binding datetime/date/time, decimal.Decimal, uuid.UUID, enum.Enum, numpy scalars (np.int64, np.float32), numpy arrays, lists/dicts meant as JSON, or custom dataclasses as parameters.

Common situations: Feeding pandas/numpy rows straight from df.itertuples(); ORMs or code ported from psycopg (adapts datetime natively) or older sqlite3 with default datetime adapters; forgetting to json.dumps a payload before INSERT.

Related errors


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