{"record":{"id":"4d5442d54233915d","repo":"tursodatabase/turso","slug":"cannot-operate-on-a-closed-connection-4d5442","errorCode":null,"errorMessage":"Cannot operate on a closed connection","messagePattern":"Cannot operate on a closed connection","errorType":"exception","errorClass":"ProgrammingError","httpStatus":null,"severity":"error","filePath":"serverless/python/turso_serverless/connection.py","lineNumber":177,"sourceCode":"    NotSupportedError = NotSupportedError\n    Warning = Warning\n\n    def __init__(\n        self,\n        session: Session,\n        *,\n        isolation_level: str | None = \"DEFERRED\",\n    ) -> None:\n        self._session = session\n        self.isolation_level = isolation_level\n        self.row_factory: Callable | type[Row] | None = None\n        self.text_factory: Any = str\n        self._autocommit_mode: object | bool = \"LEGACY\"\n        self._closed = False\n\n    def _ensure_open(self) -> None:\n        if self._closed:\n            raise ProgrammingError(\"Cannot operate on a closed connection\")\n\n    def _execute_stmt(\n        self,\n        sql: str,\n        params: list | None = None,\n        named_params: list[tuple[str, Any]] | None = None,\n        want_rows: bool = True,\n    ) -> StmtResult:\n        self._ensure_open()\n        try:\n            return self._session.execute_stmt(\n                sql, args=params, named_args=named_params, want_rows=want_rows\n            )\n        except RuntimeError as e:\n            raise _classify_error(e) from None\n\n    @property\n    def in_transaction(self) -> bool:","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/serverless/python/turso_serverless/connection.py#L159-L195","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nfor attempt in range(3):\n    try:\n        return conn.execute(sql)\n    except Exception:\n        conn.close()\n        # next iteration reuses the closed conn -> ProgrammingError\n\n# after\nfor attempt in range(3):\n    conn = create_connection(url, token)\n    try:\n        return conn.execute(sql)\n    except Exception:\n        conn.close()","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from turso_serverless.dbapi import ProgrammingError\n\ntry:\n    rows = conn.execute(sql).fetchall()\nexcept ProgrammingError as e:\n    if \"closed connection\" not in str(e):\n        raise\n    conn = create_connection(url, token)\n    rows = conn.execute(sql).fetchall()","preventionTips":["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"],"tags":["python","serverless","connection","lifecycle"],"backgroundTag":"use-after-close","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}