tursodatabase/turso · error · ProtocolError
request to {url} failed: {e.reason}
Error message
request to {url} failed: {e.reason} What it means
ProtocolError raised by Session._post (session.py:209-211) when urllib raises URLError — the request never produced an HTTP response. The reason is embedded (DNS failure, connection refused, TLS certificate verification failure, timeout). The stream is reset before raising, so any open transaction is gone.
Source
Thrown at serverless/python/turso_serverless/session.py:211
except urllib.error.HTTPError as e:
raw = e.read().decode("utf-8", errors="replace")
self._reset_stream()
message = None
try:
parsed = json.loads(raw)
if isinstance(parsed, dict):
for key in ("error", "message"):
if isinstance(parsed.get(key), str):
message = parsed[key]
break
except ValueError:
pass
if message is not None:
raise ProtocolError(f"HTTP status {e.code}: {message}") from None
raise ProtocolError(f"HTTP status {e.code}") from None
except urllib.error.URLError as e:
self._reset_stream()
raise ProtocolError(f"request to {url} failed: {e.reason}") from None
except (http.client.HTTPException, OSError) as e:
# Reading the body can fail after urlopen returned, e.g. with
# IncompleteRead on a truncated chunked response or a connection
# reset; URLError does not cover these, but they are equally
# fatal for the stream.
self._reset_stream()
raise ProtocolError(f"request to {url} failed: {e!r}") from None
def _update_stream(self, baton: Optional[str], base_url: Optional[str]) -> None:
self._baton = baton
if base_url:
self._base_url = normalize_url(base_url)
def execute_pipeline(self, requests: list[dict], track_autocommit: bool = True) -> list[dict]:
"""Execute a pipeline (section 5). When `track_autocommit` is set, a
`get_autocommit` request is appended and its answer refreshes the
cached transaction state; the returned results cover only the
caller's requests."""View on GitHub (pinned to bad083fafb)
Solutions
- Test reachability from the same environment: curl -v <database-url>
- Fix the runtime's DNS/egress or proxy env vars
- For TLS failures, install a CA bundle (certifi) and point the runtime at it
- Retry only transient reasons (timeout, reset); fail fast on DNS or certificate errors
Defensive patterns
Strategy: retry
Validate before calling
import socket, urllib.parse
def endpoint_reachable(url: str, timeout: float = 5.0) -> bool:
"""Cheap pre-flight: resolve + TCP connect before starting work."""
p = urllib.parse.urlparse(url if "://" in url else "https://" + url)
try:
socket.setdefaulttimeout(timeout)
socket.create_connection((p.hostname, p.port or 443), timeout=timeout).close()
return True
except OSError:
return False Try / catch
import time
from turso_serverless.protocol import ProtocolError
_TRANSIENT = ("timed out", "Connection reset", "temporarily unavailable")
def run(conn, sql, params=()):
for attempt in range(3):
try:
return conn.execute(sql, params).fetchall()
except ProtocolError as e:
reason = str(e)
if "request to" not in reason or not any(t in reason for t in _TRANSIENT):
raise # DNS/cert failures are deterministic: fail fast
time.sleep(0.5 * 2 ** attempt) Prevention
- Check egress/DNS permissions of the runtime (sandboxed functions often block them)
- Audit http_proxy/https_proxy env vars — urllib honors them silently
- Install a CA bundle (certifi) in slim containers for TLS verification
- Retry only transient reasons (timeout/reset); DNS and cert errors need config fixes
When it happens
Trigger: Hostname typo or nonexistent database host; DNS resolution failing inside the runtime; firewall or sandbox blocking outbound egress; TLS errors from self-signed certs or missing CA bundles; proxy env vars (http_proxy/https_proxy) misdirecting urllib.
Common situations: Serverless functions (Lambda, Cloud Run jobs) without network egress permissions; local dev behind VPN DNS; containers with an incomplete CA store; corporate proxy variables set in the environment that urllib picks up automatically.
Related errors
- Cannot operate on a closed connection
- Cannot operate on a closed connection
- request to {url} failed: {e!r}
- database must be connected before execution the function
- getAllRows: exceeded ${MAX_IO_RETRIES} IO retries
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/67cd714e2679feb7.
Report an issue: GitHub.