{"record":{"id":"3c5a9bde6bd2ed46","repo":"pola-rs/polars","slug":"cannot-return-a-frame-before-executing-a-query","errorCode":null,"errorMessage":"cannot return a frame before executing a query","messagePattern":"cannot return a frame before executing a query","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/database/_executor.py","lineNumber":596,"sourceCode":"        return self\n\n    def to_polars(\n        self,\n        *,\n        iter_batches: bool = False,\n        batch_size: int | None = None,\n        schema_overrides: SchemaDict | None = None,\n        infer_schema_length: int | None = N_INFER_DEFAULT,\n    ) -> DataFrame | Iterator[DataFrame]:\n        \"\"\"\n        Convert the result set to a DataFrame.\n\n        Wherever possible we try to return arrow-native data directly; only\n        fall back to initialising with row-level data if no other option.\n        \"\"\"\n        if self.result is None:\n            msg = \"cannot return a frame before executing a query\"\n            raise RuntimeError(msg)\n\n        can_close = self.can_close_cursor\n\n        if defer_cursor_close := (iter_batches and can_close):\n            self.can_close_cursor = False\n\n        for frame_init in (\n            self._from_arrow,  # init from arrow-native data (where support exists)\n            self._from_rows,  # row-wise fallback (sqlalchemy, dbapi2, pyodbc, etc)\n        ):\n            frame = frame_init(\n                batch_size=batch_size,\n                iter_batches=iter_batches,\n                schema_overrides=schema_overrides,\n                infer_schema_length=infer_schema_length,\n            )\n            if frame is not None:\n                if defer_cursor_close:","sourceCodeStart":578,"sourceCodeEnd":614,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/io/database/_executor.py#L578-L614","documentation":"Raised by ConnectionExecutor.to_polars() when self.result is None, meaning no result set exists to convert. The executor requires that .execute(query) has run and produced a fetchable result before a DataFrame can be materialized. Statements that return no rows (DDL/DML like CREATE/INSERT/UPDATE) or calling to_polars() before execute() both leave result unset.","triggerScenarios":"Calling pl.read_database() with a non-SELECT statement (e.g. 'CREATE TABLE ...' or an INSERT) on a DBAPI/SQLAlchemy connection; using ConnectionExecutor directly and calling .to_polars() without a prior successful .execute(); executing a query on a cursor whose .fetch*() yields nothing the executor can consume.","commonSituations":"Scripts that run setup DDL and read-back in one loop through read_database; migrating code from a driver's raw cursor API where execute() alone was harmless; calling read_database on a connection whose dialect/driver returns None from cursor.description.","solutions":["Only pass queries that return a result set (SELECT / SHOW / EXPLAIN) to read_database; run DDL and DML through the connection's own cursor or engine.execute","If using ConnectionExecutor directly, always call cx.execute(query) and check cx.result is not None before cx.to_polars(...)","For row-affecting statements, wrap them in a SELECT that returns rows, or read with 'RETURNING'-style clauses where the backend supports them"],"exampleFix":"# before\ndf = pl.read_database(\"INSERT INTO t VALUES (1)\", connection)  # no result set\n\n# after\nwith connection.cursor() as cur:\n    cur.execute(\"INSERT INTO t VALUES (1)\")\ndf = pl.read_database(\"SELECT * FROM t\", connection)","handlingStrategy":"try-catch","validationCode":"from polars.io.database._executor import ConnectionExecutor\n\nwith ConnectionExecutor(conn) as cx:\n    cx.execute(query=query)\n    if cx.result is None:\n        # statement produced no result set; nothing to frame\n        ...","typeGuard":null,"tryCatchPattern":"try:\n    df = pl.read_database(sql, conn)\nexcept RuntimeError as e:\n    if \"cannot return a frame\" in str(e):\n        # sql was DDL/DML with no result set — run it via raw cursor instead\n        with conn.cursor() as cur:\n            cur.execute(sql)\n        df = None\n    else:\n        raise","preventionTips":["Only feed SELECT-like statements to read_database; keep DDL/DML on the raw cursor","Keep a helper that classifies SQL (SELECT vs other) before choosing the read path","When using ConnectionExecutor directly, always pair execute() with a result check"],"tags":["database","runtime","api-misuse","result-set"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}