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
- Convert before binding: datetime -> isoformat string, Decimal -> str or float, UUID -> str, JSON objects -> json.dumps(...)
- Cast numpy scalars with int()/float() and arrays with .tolist() at the row boundary
- 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
- Normalize every row through one adapt() function at the ingestion boundary
- Cast numpy scalars with int()/float() and arrays with .tolist() before binding
- json.dumps objects/lists yourself — the driver never auto-serializes
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
- infinite float values cannot be sent over the protocol
- Named parameters are not supported; use positional parameter
- invalid value in server response: {e}
- cursor response body ended before the cursor response line
- invalid cursor response: {e}
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/bc6f68e76bc2bf4c.
Report an issue: GitHub.