pola-rs/polars · error · RuntimeError

Event loop stopped before Future completed.

Error message

Event loop stopped before Future completed.

What it means

polars vendors nest_asyncio2 (polars/_utils/nest_asyncio.py) so pl.read_database can drive async drivers from synchronous code inside an already-running loop (polars/io/database/_utils.py). The patched run_until_complete loops _run_once until the future completes or _stopping is set; if the loop is stopped (loop.stop(), KeyboardInterrupt in the nested run, framework interactions) while the query future is incomplete, this RuntimeError is raised.

Source

Thrown at py-polars/src/polars/_utils/nest_asyncio.py:283

    def run_forever(self):
        with manage_run(self), manage_asyncgens(self):
            while True:
                self._run_once()
                if self._stopping:
                    break
        self._stopping = False

    def run_until_complete(self, future):
        with manage_run(self):
            f = asyncio.ensure_future(future, loop=self)
            if f is not future:
                f._log_destroy_pending = False
            while not f.done():
                self._run_once()
                if self._stopping:
                    break
            if not f.done():
                raise RuntimeError("Event loop stopped before Future completed.")
            return f.result()

    def _run_once(self):
        """
        Simplified re-implementation of asyncio's _run_once that
        runs handles as they become ready.
        """
        scheduled = self._scheduled
        while scheduled and scheduled[0]._cancelled:
            heappop(scheduled)

        timeout = (
            0
            if self._ready or self._stopping
            else min(max(scheduled[0]._when - self.time(), 0), 86400)
            if scheduled
            else None
        )

View on GitHub (pinned to df599052da)

Solutions

  1. Let the nested run finish; avoid calling loop.stop()/close() while read_database executes
  2. Call read_database from a plain script or thread with no running loop, so the patch is bypassed
  3. Use the async driver natively: await the connector in an async task and build the DataFrame with pl.from_arrow/pl.from_dicts
  4. Upgrade polars — the vendored nest_asyncio2 receives fixes across releases

Example fix

# before (sync call inside running loop, Jupyter)
df = pl.read_database("SELECT * FROM t", connection=async_conn)

# after (await the async driver directly)
df = pl.from_arrow(await async_conn.fetch_arrow_table("SELECT * FROM t"))
Defensive patterns

Strategy: try-catch

Try / catch

try:
    df = pl.read_database(qry, connection=conn)
except RuntimeError as exc:
    if "Event loop stopped before Future completed" in str(exc):
        # loop was stopped mid-query; retry on a fresh default loop or fail cleanly
        df = pl.read_database(qry, connection=sync_conn)
    else:
        raise

Prevention

When it happens

Trigger: pl.read_database(...) with an asyncio-based connector inside Jupyter when the loop is stopped or the cell is interrupted mid-query; another library calling loop.stop() during the nested run; mixing asyncio.run() with the patched loop.

Common situations: Notebooks with async database connectors; Ctrl-C during read_database; tornado/IPython loop management colliding with the nested run; server frameworks stopping the shared loop during shutdown while a read is in flight.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/9e76dddd7d7c6439. Report an issue: GitHub.