run-llama/llama_index · error · RuntimeError

Detected nested async. Please use nest_asyncio.apply() to al

Error message

Detected nested async. Please use nest_asyncio.apply() to allow nested event loops.Or, use async entry methods like `aquery()`, `aretriever`, `achat`, etc.

What it means

async_utils runs a coroutine from sync code by checking the thread's event loop. When a running loop already exists in this thread (sync code called from inside async) AND asyncio.run also fails (loop policy/thread constraints), it concludes the call is nested async and raises. The message itself offers the two fixes: nest_asyncio.apply() or using the async entry points (aquery, aretrieve, achat, ...).

Source

Thrown at llama-index-core/llama_index/core/async_utils.py:74

                asyncio.set_event_loop(new_loop)
                try:
                    return ctx.run(new_loop.run_until_complete, coro)
                finally:
                    new_loop.close()

            with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
                future = executor.submit(run_coro_in_thread)
                return future.result()
        else:
            # If we're here, there's an existing loop but it's not running
            return loop.run_until_complete(coro)

    except RuntimeError as e:
        # If we can't get the event loop, we're likely in a different thread
        try:
            return asyncio.run(coro)
        except RuntimeError as e:
            raise RuntimeError(
                "Detected nested async. Please use nest_asyncio.apply() to allow nested event loops."
                "Or, use async entry methods like `aquery()`, `aretriever`, `achat`, etc."
            )


def run_async_tasks(
    tasks: List[Coroutine],
    show_progress: bool = False,
    progress_bar_desc: str = "Running async tasks",
) -> List[Any]:
    """Run a list of async tasks."""
    tasks_to_execute: List[Any] = tasks
    if show_progress:
        try:
            import nest_asyncio
            from tqdm.asyncio import tqdm

            # jupyter notebooks already have an event loop running

View on GitHub (pinned to afd0fef371)

Solutions

  1. Switch the call site to the async variant: await query_engine.aquery(...), await retriever.aretrieve(...), await llm.achat(...).
  2. In Jupyter, run `import nest_asyncio; nest_asyncio.apply()` once at startup (nest_asyncio is already a llama-index-core dependency).
  3. For server apps, push blocking sync calls to a thread: `await asyncio.to_thread(engine.query, q)`.

Example fix

# before (inside async def or Jupyter)
response = query_engine.query("what is this?")  # RuntimeError

# after
response = await query_engine.aquery("what is this?")

# Jupyter alternative
import nest_asyncio
nest_asyncio.apply()
response = query_engine.query("what is this?")
Defensive patterns

Strategy: validation

Validate before calling

import asyncio

def in_running_loop() -> bool:
    try:
        return asyncio.get_running_loop() is not None
    except RuntimeError:
        return False

# if in_running_loop(): call aquery/aretrieve/achat instead of sync versions

Try / catch

try:
    result = engine.query(q)
except RuntimeError as e:
    if "nested async" in str(e):
        raise RuntimeError("call await engine.aquery(q) instead") from e
    raise

Prevention

When it happens

Trigger: Calling a sync wrapper such as query_engine.query(...) or retriever.retrieve(...) from inside an async function / Jupyter cell (Jupyter already runs a loop), or calling .query() inside a workflow @step; the code path funnels through run_async_tasks which cannot block on the running loop.

Common situations: Notebooks (IPython has an active event loop); FastAPI/asyncio handlers calling sync llama-index APIs; mixing sync examples into async codebases; calling vector store or index construction methods synchronously inside async code.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/882829db61cdd562. Report an issue: GitHub.