{"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/244cde92a7df7f9b8b8b7a4075c35a12977e303e/serverless/python/turso_serverless/protocol.py#L32-L68","documentation":"encode_value() serializes one bind parameter for the SQL-over-HTTP protocol. It supports None, bool and int (sent as integers), finite floats, str, and bytes/bytearray (base64). NaN encodes as null because SQLite binds NaN as NULL; infinite floats are rejected with a separate ValueError. Every other Python object falls through to TypeError('Unsupported value type: ...').","triggerScenarios":"Binding datetime.datetime, date, Decimal, UUID, numpy scalars, lists, dicts, or custom classes as a statement parameter in the serverless client.","commonSituations":"Passing domain objects without an ORM; pandas/numpy values leaking into parameter lists; monetary Decimal amounts; expecting JSON-style auto-serialization of nested structures.","solutions":["Convert before binding: dt.isoformat() for datetimes, str() for UUID, float(Decimal), int(np.int64)","Serialize structured values (lists/dicts) to a JSON string yourself","Restrict parameters to None/int/float/str/bytes at your API boundary"],"exampleFix":"# before\nconn.execute(\"INSERT INTO events VALUES (?)\", [datetime.now()])\n\n# after\nconn.execute(\"INSERT INTO events VALUES (?)\", [datetime.now().isoformat()])","handlingStrategy":"type-guard","validationCode":"def encode_param(value):\n    if isinstance(value, (datetime, date)):\n        return value.isoformat()\n    if isinstance(value, Decimal):\n        return float(value)\n    return value\n\nparams = [encode_param(p) for p in params]\nconn.execute(sql, params)","typeGuard":"def is_supported_bind_value(v) -> bool:\n    # None, bool/int, finite float, str, bytes/bytearray are encodable\n    if isinstance(v, (int, float, str, bytes, bytearray)):\n        return not (isinstance(v, float) and math.isinf(v))\n    return v is None","tryCatchPattern":"try:\n    conn.execute(sql, params)\nexcept TypeError as e:\n    if \"Unsupported value type\" in str(e):\n        params = [str(p) if not is_supported_bind_value(p) else p for p in params]\n        conn.execute(sql, params)\n    else:\n        raise","preventionTips":["Convert datetimes, Decimals, UUIDs, and numpy scalars at the edge of your data layer","Never assume auto-serialization of objects or containers","Wrap parameter building in one function you can test"],"tags":["python","binding","serialization","type-error","protocol"],"backgroundTag":"unsupported-bind-parameter-type","analyzedSha":"244cde92a7df7f9b8b8b7a4075c35a12977e303e","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}