tursodatabase/turso · error · ProgrammingError
Cannot operate on a closed cursor
Error message
Cannot operate on a closed cursor
What it means
Every public method on the blocking Cursor (execute, executemany, fetchone, fetchmany, fetchall, ...) first calls _ensure_open(). Once cursor.close() has run, the _closed flag is set in a finally block and every subsequent call raises ProgrammingError('Cannot operate on a closed cursor'). The flag is set even if finalizing the active statement inside close() failed, so close() always wins.
Solutions
- Create a new cursor from the connection and re-run the statement
- Move all fetches before the close() call in your control flow
- Stop caching cursors; create one per unit of work and let it die with the function
Example fix
// before
cur = conn.cursor()
cur.execute("SELECT * FROM t")
cur.close()
rows = cur.fetchall() # ProgrammingError
// after
cur = conn.cursor()
cur.execute("SELECT * FROM t")
rows = cur.fetchall()
cur.close() Defensive patterns
Strategy: try-catch
Try / catch
from turso.lib import ProgrammingError
try:
rows = cur.fetchall()
except ProgrammingError as e:
if "closed cursor" not in str(e):
raise
cur = conn.cursor()
cur.execute(sql)
rows = cur.fetchall() Prevention
- Fetch all rows before closing the cursor
- Keep cursor creation, use, and close inside one function or context block
- Treat cursors as single-use objects, not long-lived state
When it happens
Trigger: Calling any cursor method after cursor.close(); reusing a cursor stored on an object that was cleaned up earlier; a generator or lazy iterator that resumes after the code that owned the cursor called close().
Common situations: Cleanup in a finally block that runs before the last fetch; one cursor cached as an instance attribute and closed by a helper method; test teardown closing cursors while assertions still read them.
Related errors
- Cannot operate on a closed cursor
- Cannot operate on a closed connection
- Cannot operate on a closed connection
- executemany() requires a single DML…
- Named parameters are not supported; use positional…
AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-20).
Data as JSON: /api/errors/d9d1825d866dabb4.
Report an issue: GitHub.
Appendix: source
Thrown at bindings/python/turso/lib.py:811
def close(self) -> None:
if self._closed:
return
try:
# Finalize any active statement to ensure completion.
if self._active_stmt is not None:
try:
self._active_stmt.finalize()
except Exception:
pass
finally:
self._active_stmt = None
self._active_has_rows = False
self._closed = True
def _ensure_open(self) -> None:
if self._closed:
raise ProgrammingError("Cannot operate on a closed cursor")
@property
def description(self) -> tuple[tuple[str, None, None, None, None, None, None], ...] | None:
return self._description
@property
def lastrowid(self) -> int | None:
return self._lastrowid
@property
def rowcount(self) -> int:
return self._rowcount
def _reset_last_result(self) -> None:
# Ensure any previous statement is finalized to not leak resources
if self._active_stmt is not None:
try:
self._active_stmt.finalize()View on GitHub (pinned to c1e5928725)