cocoindex-io/cocoindex · error · RuntimeError
async_to_sync_iter must not be called from a running event l
Error message
async_to_sync_iter must not be called from a running event loop
What it means
async_to_sync_iter bridges an async iterator into a synchronous one using a background thread, which must own the event loop; calling it from inside a running event loop would deadlock (the sync iteration would block the loop the async side needs). It therefore raises this RuntimeError eagerly.
Source
Thrown at python/cocoindex/connectorkits/async_adapters.py:139
Raises:
RuntimeError: If called from within a running event loop.
Any exception raised by the async iterator is re-raised.
Example:
>>> async def async_generator(start: int, end: int):
... for i in range(start, end):
... yield i
...
>>> for value in async_to_sync_iter(lambda: async_generator(0, 5)):
... print(value)
"""
try:
_asyncio.get_running_loop()
except RuntimeError:
pass # No running loop, which is what we want
else:
raise RuntimeError(
"async_to_sync_iter must not be called from a running event loop"
)
q: _queue.Queue[tuple[bool, _T | Exception]] = _queue.Queue(maxsize=max_queue_size)
stop_event = _threading.Event()
def producer() -> None:
async def run_async() -> None:
try:
async for item in async_iter_fn():
if stop_event.is_set():
break
q.put((False, item))
except Exception as e: # pylint: disable=broad-except
q.put((True, e))
finally:
q.put((True, StopIteration()))
View on GitHub (pinned to e84aa99b32)
Solutions
- Move the sync iteration out of async code — call it from a plain sync function or a separate thread
- Inside async code, iterate the async iterator directly with `async for`
- If a sync API requires the data, materialize it first with asyncio.run() in a thread outside the loop (or run_in_executor)
- For scripts, ensure no outer event loop is running when using the sync adapter
Example fix
// before
async def main():
for item in async_to_sync_iter(agen()): # RuntimeError
...
// after
async def main():
async for item in agen():
... Defensive patterns
Strategy: validation
Validate before calling
import asyncio
if asyncio.get_event_loop().is_running():
raise RuntimeError("use async iteration inside async code") Type guard
def can_use_sync_iter() -> bool:
try:
asyncio.get_running_loop()
return False
except RuntimeError:
return True Try / catch
try:
for item in async_to_sync_iter(aiter):
process(item)
except RuntimeError as e:
if "running event loop" in str(e):
run_in_thread(lambda: consume_sync(aiter)) Prevention
- Only use sync adapters from threads without a running event loop
- Inside async code use `async for` directly
- In Jupyter/FastAPI, run sync adapters via run_in_executor or a worker thread
When it happens
Trigger: Calling async_to_sync_iter(...) (directly or via __iter__ of the adapter) inside an async def function or any code running on an active asyncio loop — e.g. `for x in async_to_sync_iter(aiter())` inside async code.
Common situations: Using the sync adapter inside a Jupyter notebook or FastAPI handler (both run a loop); wrapping an async iterator in sync code that was itself made async; mixing asyncio.run with an outer loop.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- LiveMap supports a single active watch() at a time.
- CocoIndex timeout deadline exceeded
- Cannot use sync 'with coco.runtime()' from within an async e
- Memo state function returned an awaitable from a sync contex
- {self._label} supports a single active watch() at a time.
AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08).
Data as JSON: /api/errors/3136fa72bece66d3.
Report an issue: GitHub.