{"record":{"id":"0b1982df930a8fec","repo":"tursodatabase/turso","slug":"invalid-rowid-in-server-response-e","errorCode":null,"errorMessage":"invalid rowid in server response: {e}","messagePattern":"invalid rowid in server response: (.+?)","errorType":"exception","errorClass":"ProtocolError","httpStatus":null,"severity":"error","filePath":"serverless/python/turso_serverless/session.py","lineNumber":129,"sourceCode":"        else:\n            self._in_probe = False\n            self.result.columns = [c.get(\"name\") or \"\" for c in entry.get(\"cols\") or []]\n\n    def _row(self, entry: dict) -> None:\n        if self._in_probe or self.step_error is not None:\n            return\n        self.result.rows.append(tuple(decode_value(v) for v in entry.get(\"row\") or []))\n\n    def _step_end(self, entry: dict) -> None:\n        if self._in_probe:\n            return\n        self.result.affected_rows = entry.get(\"affected_row_count\") or 0\n        rowid = entry.get(\"last_insert_rowid\")\n        if rowid is not None:\n            try:\n                self.result.last_insert_rowid = int(rowid)\n            except (TypeError, ValueError) as e:\n                raise ProtocolError(f\"invalid rowid in server response: {e}\") from None\n\n    def _step_error(self, entry: dict) -> None:\n        if entry.get(\"step\") == _PROBE_STEP:\n            self.probe_unreliable = True\n        elif self.step_error is None:\n            self.step_error = _server_error(entry.get(\"error\"))\n        self._in_probe = False\n\n\nclass Session:\n    \"\"\"Manages one server-side stream: the baton, the base URL, and the\n    server-reported transaction state.\"\"\"\n\n    def __init__(\n        self,\n        url: str,\n        auth_token: Optional[str] = None,\n        remote_encryption_key: Optional[str] = None,","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/serverless/python/turso_serverless/session.py#L111-L147","documentation":"ProtocolError raised by _CursorDecoder._step_end (session.py:120-129) when a step_end entry carries a last_insert_rowid that int() cannot convert — a non-numeric string, list, or dict. The server violated the section 7.2 entry shape. Because it is raised inside execute_stmt's try block, the stream is reset before the error reaches the caller.","triggerScenarios":"A server build emits last_insert_rowid in an unexpected format (stringified, nested object); a JSON-transforming intermediary stringifies or wraps numeric fields.","commonSituations":"Version skew between driver and server; API gateways that 'normalize' numbers to strings; alternative server implementations that are not fully protocol-conformant.","solutions":["Align the turso_serverless client version with the server release","Remove any gateway that rewrites numeric fields in response bodies","Capture the raw response and report the protocol violation — this cannot be fixed client-side"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from turso_serverless.protocol import ProtocolError\n\ntry:\n    cur = conn.execute(\"INSERT INTO t VALUES (?)\", (x,))\nexcept ProtocolError as e:\n    if \"invalid rowid\" not in str(e):\n        raise\n    # the statement may have committed: verify before any retry\n    got = conn.execute(\"SELECT 1 FROM t WHERE x = ?\", (x,)).fetchone()\n    if got is None:\n        conn.execute(\"INSERT INTO t VALUES (?)\", (x,))","preventionTips":["Keep driver and server versions locked together in deployment","Ban JSON-transforming gateways on the database path","Capture and report the raw response — this is a server/intermediary spec violation"],"tags":["python","protocol","rowid","response-parsing"],"backgroundTag":"malformed-server-response","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}