Skip to content

fix: keep the original error when a curator run fails inside a running event loop - #750

Open
TanishGudise wants to merge 1 commit into
bespokelabsai:mainfrom
TanishGudise:fix/run-in-event-loop-error
Open

TanishGudise wants to merge 1 commit into
bespokelabsai:mainfrom
TanishGudise:fix/run-in-event-loop-error

Conversation

@TanishGudise

Copy link
Copy Markdown

Closes #746

When an event loop was already running (Jupyter, Colab), run_in_event_loop called asyncio.run(coroutine) inside a try that caught RuntimeError. If the coroutine raised a RuntimeError, the handler swallowed it and asyncio.run was called again with the same finished coroutine, so the user saw RuntimeError: cannot reuse already awaited coroutine instead of the real error (for example batch mode's "None of the submitted batches completed successfully").

Now only the RuntimeError from asyncio.get_running_loop() is caught. nest_asyncio.apply() runs in the else branch, and the coroutine runs once, after the try block, so its errors aren't chained to the "no running event loop" error (the case #99 fixed).

Tests (tests/unittests/test_event_loop.py)

  • Inside a running loop, the coroutine's own RuntimeError reaches the caller and the coroutine runs once (fails on main with "cannot reuse already awaited coroutine").
  • Without a running loop, the error reaches the caller unchanged and isn't chained (passes on main and with the fix).
  • A result is returned inside a running loop (passes on main and with the fix).

The tests also catch two regressions: moving asyncio.run back into the except block fails the no-loop test, and dropping nest_asyncio.apply() fails both in-loop tests.

Manual check: in a real Jupyter kernel (ipykernel 7.4.0), the cell gets RuntimeError('cannot reuse already awaited coroutine') on main and the original message with this change.

Without network access, the full test suite has no new failures compared with main: the offline failures are tests that need the network, plus test_simple_code_execution_local, which also fails locally on main.

CI note: CI on main is currently red for reasons unrelated to this PR: test_basic_reasoning and test_batch_reasoning fail because litellm 1.83.7 doesn't map claude-3-7-sonnet-20250219, and total coverage is 78%, under --cov-fail-under=80 (run 37956501158 on 31abdb7). Once CI runs here (first-time contributor runs wait for approval), those failures will show on this PR too until main is fixed.

🤖 Generated with Claude Code

…g event loop

When an event loop was already running (Jupyter, Colab), run_in_event_loop
called asyncio.run inside a try block that caught RuntimeError. If the
coroutine raised a RuntimeError, the handler swallowed it and asyncio.run was
called again with the same, already finished coroutine, so the user saw
"RuntimeError: cannot reuse already awaited coroutine" instead of the real
error (for example batch mode's "None of the submitted batches completed
successfully").

Only catch the RuntimeError from get_running_loop() and run the coroutine once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@strix-security

strix-security Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Strix Security Review

No security issues found.

Review summary

Reviewed the two changed files: the run_in_event_loop refactor in src/bespokelabs/curator/request_processor/event_loop.py and the new tests/unittests/test_event_loop.py. The change solely restructures error handling so the coroutine's own RuntimeError propagates unchanged and the coroutine executes exactly once, instead of being swallowed and re-run. No security-relevant behavior is introduced: there is no untrusted input, no dangerous sink, and no changes to authentication, authorization, cryptography, or secrets handling. The test file only verifies that error-propagation behavior. No vulnerabilities found.

Updated for 17db896.


Reviewed by Strix
Re-run review · Configure security review settings

@entelligence-ai-pr-reviews

Copy link
Copy Markdown

EntelligenceAI PR Summary

Updated run_in_event_loop so event-loop detection and coroutine execution are handled separately, ensuring coroutine RuntimeErrors propagate unchanged instead of triggering a second attempt to await the same coroutine. This preserves the original failure message in environments with or without an existing event loop, including Jupyter and Colab. Added tests covering error propagation, coroutine execution count, and successful results inside a running loop.

flowchart TD
    classDef newBehavior fill:#dcfce7,stroke:#16a34a,color:#14532d;
    A["run_in_event_loop"] --> B["Check for running event loop"]
    B --> C{"Loop already running?"}
    C -->|No| D["Call asyncio.run once"]
    C -->|Yes| E["Apply nest_asyncio"]
    E --> D
    D --> F["Coroutine executes once"]
    F --> G["Return result or propagate original error"]
    class A,B,C,D,E,F,G newBehavior;
Loading

🟢 Green = new or changed in this PR


Review Scorecard

Dimension Rating Basis
Code Quality ●●●●● 5/5 — Excellent no critical/significant findings — rated Excellent
Blast Radius Low changed symbols are referenced only within their own file(s); no high-impact surface touched, 1 file(s) / ~17 line(s) changed (size only — not a blast signal)
Merge Confidence ●●●●● 5/5 — Safe to Merge code quality 5/5 × Low blast radius

Safe to merge with a low blast radius; code quality rated Excellent (5/5): the review found nothing to flag.

Key Findings:

  • No critical or significant issues found
Evaluated against
  • 2/2 changed files reviewed
  • criteria: correctness, security & access control, robustness & error handling, concurrency & data integrity, repo conventions / steering docs
  • steering docs: none found in repo

@entelligence-ai-pr-reviews

Copy link
Copy Markdown

Walkthrough

Refines run_in_event_loop so event-loop detection is separated from coroutine execution, preventing coroutine-raised RuntimeError values from being incorrectly chained while retaining nested-loop support. New tests validate error propagation, error context, and successful execution in both standard and already-running loop environments.

Changes

Files

  • src/bespokelabs/curator/request_processor/event_loop.py

Updated run_in_event_loop to catch only the RuntimeError from asyncio.get_running_loop(), apply nest_asyncio when a loop is already running, and call asyncio.run(coroutine) outside the exception handler. This preserves nested-loop support and prevents coroutine exceptions from being chained to loop-detection errors.

Files

  • tests/unittests/test_event_loop.py

Added coverage for exception propagation and unchained original errors when invoked with and without a running event loop. Tests also verify successful coroutine execution and forwarding of the result 42 in a running-loop environment.

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.

In Jupyter/Colab, a RuntimeError raised inside a curator run is replaced by "RuntimeError: cannot reuse already awaited coroutine"

1 participant