tursodatabase/turso · error · ProgrammingError
Cannot operate on a closed connection
Error message
Cannot operate on a closed connection
What it means
In the serverless client, Connection._ensure_open() raises ProgrammingError('Cannot operate on a closed connection') before _execute_stmt sends anything over the HTTP session. close() sets _closed, and every execution path (queries, DML, commit) goes through the same guard, so a closed serverless connection refuses all work locally without a network round trip.
Solutions
- Create a fresh Connection (a new session) for further work
- Restructure retry loops to reopen the connection before each attempt
- Keep all connection usage inside its context-manager block
Example fix
# before
for attempt in range(3):
try:
return conn.execute(sql)
except Exception:
conn.close()
# next iteration reuses the closed conn -> ProgrammingError
# after
for attempt in range(3):
conn = create_connection(url, token)
try:
return conn.execute(sql)
except Exception:
conn.close() Defensive patterns
Strategy: try-catch
Try / catch
from turso_serverless.dbapi import ProgrammingError
try:
rows = conn.execute(sql).fetchall()
except ProgrammingError as e:
if "closed connection" not in str(e):
raise
conn = create_connection(url, token)
rows = conn.execute(sql).fetchall() Prevention
- Open a connection per handler or request scope; do not share across closures
- Retry loops must rebuild the connection each attempt
- Keep usage inside the connection's context block so scope is obvious
When it happens
Trigger: conn.execute(...) after conn.close(); using the connection after its context manager exited (__aexit__/__exit__ calls close); a retry loop that closes the connection on one error and then continues the loop with the same object.
Common situations: Generic retry wrappers that 'clean up' on failure then retry with the dead connection; serverless handlers keeping a module-level connection past its lifetime; cleanup ordering bugs in request scopes.
Related errors
- Cannot operate on a closed connection
- batch mode must be one of
- batch response does not have one result and one error per…
- batch response is missing the result for statement
- batch statement must be a SQL string or a (sql, parameters)…
AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-20).
Data as JSON: /api/errors/4d5442d54233915d.
Report an issue: GitHub.
Appendix: source
Thrown at serverless/python/turso_serverless/connection.py:177
NotSupportedError = NotSupportedError
Warning = Warning
def __init__(
self,
session: Session,
*,
isolation_level: str | None = "DEFERRED",
) -> None:
self._session = session
self.isolation_level = isolation_level
self.row_factory: Callable | type[Row] | None = None
self.text_factory: Any = str
self._autocommit_mode: object | bool = "LEGACY"
self._closed = False
def _ensure_open(self) -> None:
if self._closed:
raise ProgrammingError("Cannot operate on a closed connection")
def _execute_stmt(
self,
sql: str,
params: list | None = None,
named_params: list[tuple[str, Any]] | None = None,
want_rows: bool = True,
) -> StmtResult:
self._ensure_open()
try:
return self._session.execute_stmt(
sql, args=params, named_args=named_params, want_rows=want_rows
)
except RuntimeError as e:
raise _classify_error(e) from None
@property
def in_transaction(self) -> bool:View on GitHub (pinned to c1e5928725)