{"record":{"id":"bc6f68e76bc2bf4c","repo":"tursodatabase/turso","slug":"unsupported-value-type-type-value-name","errorCode":null,"errorMessage":"Unsupported value type: {type(value).__name__}","messagePattern":"Unsupported value type: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"serverless/python/turso_serverless/protocol.py","lineNumber":50,"sourceCode":"    if value is None:\n        return {\"type\": \"null\"}\n    if isinstance(value, bool):\n        return {\"type\": \"integer\", \"value\": str(int(value))}\n    if isinstance(value, int):\n        return {\"type\": \"integer\", \"value\": str(value)}\n    if isinstance(value, float):\n        # The protocol forbids sending non-finite floats (section 8.2).\n        if math.isnan(value):\n            # SQLite binds NaN as NULL; JSON cannot carry it.\n            return {\"type\": \"null\"}\n        if math.isinf(value):\n            raise ValueError(\"infinite float values cannot be sent over the protocol\")\n        return {\"type\": \"float\", \"value\": value}\n    if isinstance(value, str):\n        return {\"type\": \"text\", \"value\": value}\n    if isinstance(value, (bytes, bytearray)):\n        return {\"type\": \"blob\", \"base64\": base64.b64encode(value).decode(\"ascii\")}\n    raise TypeError(f\"Unsupported value type: {type(value).__name__}\")\n\n\ndef decode_value(pv: dict) -> Any:\n    \"\"\"Decode a protocol value (section 8) to a Python value.\"\"\"\n    try:\n        typ = pv[\"type\"]\n        if typ == \"null\":\n            return None\n        if typ == \"integer\":\n            return int(pv[\"value\"])\n        if typ == \"float\":\n            raw = pv[\"value\"]\n            # A null value encodes a non-finite float (section 8.2); the\n            # spec says to decode it as NaN.\n            if raw is None:\n                return math.nan\n            return float(raw)\n        if typ == \"text\":","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/serverless/python/turso_serverless/protocol.py#L32-L68","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ncur.execute(\"INSERT INTO events(ts, payload) VALUES (?, ?)\", (event_dt, payload_dict))\n\n// after\ncur.execute(\"INSERT INTO events(ts, payload) VALUES (?, ?)\", (event_dt.isoformat(), json.dumps(payload_dict)))","handlingStrategy":"type-guard","validationCode":"import datetime as dt, decimal, uuid\n\n\ndef adapt(v):\n    \"\"\"Convert common Python types to the protocol's supported set.\"\"\"\n    if isinstance(v, (dt.datetime, dt.date)):\n        return v.isoformat()\n    if isinstance(v, decimal.Decimal):\n        return str(v)\n    if isinstance(v, uuid.UUID):\n        return str(v)\n    if isinstance(v, (list, dict)):\n        return json.dumps(v)\n    if type(v).__module__ == \"numpy\":\n        return v.item()\n    return v\n\n\nparams = tuple(adapt(p) for p in params)","typeGuard":"def is_supported_value(v) -> bool:\n    \"\"\"Only these types cross the wire (bool/int overlap is fine).\"\"\"\n    return v is None or isinstance(v, (bool, int, float, str, bytes, bytearray))","tryCatchPattern":"try:\n    cur.execute(sql, params)\nexcept TypeError as e:\n    if not str(e).startswith(\"Unsupported value type\"):\n        raise\n    params = tuple(adapt(p) for p in params)  # adapt() as in validationCode\n    cur.execute(sql, params)","preventionTips":["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"],"tags":["python","protocol","type-conversion","parameters","serialization"],"backgroundTag":"unsupported-parameter-type","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}