fix: #2929 surface run-loop exceptions after stream_events() completes#2931
Merged
seratch merged 1 commit intoApr 18, 2026
Merged
Conversation
Two changes to RunResultStreaming: 1. Add a `run_loop_exception` property that returns the background run-loop task's exception (or None) after the stream ends. Users no longer need to reach into `result.run_loop_task.exception()` to detect silent failures (e.g. early sandbox init errors). 2. Call `_check_errors()` in the non-cancelled `finally` path of `stream_events()` immediately after `_await_task_safely(run_loop_task)`. Previously, exceptions that raced past the queue sentinel were silently swallowed because `_await_task_safely` catches all exceptions and no subsequent check re-examined the task state. Closes openai#2929
seratch
approved these changes
Apr 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #2929.
When
Runner.run_streamed()is used and the background run loop raises an exception early (e.g. during sandbox initialisation, before any stream event is produced), the failure could be silently swallowed — leaving callers with a result that looks clean (final_output=None,new_items=[]) but is actually broken.This PR makes two minimal, additive changes to
RunResultStreaming:1.
run_loop_exceptionpropertyA new read-only property that directly exposes the background task's exception after the stream ends, without requiring callers to reach into internal task state:
Returns
Noneif the run completed without error, has not yet finished, or was cancelled.2.
_check_errors()call in thestream_events()finally block_await_task_safely()intentionally swallows all exceptions (they're supposed to be surfaced via_stored_exception). But_check_errors()was not called after_await_task_safely(run_loop_task)in the non-cancelled finally path. This meant a run-loop failure that raced past the sentinel was not transferred into_stored_exception, so the finalif self._stored_exception: raisenever fired.Adding
self._check_errors()afterawait self._await_task_safely(self.run_loop_task)closes this gap.Changes
src/agents/result.py— addsrun_loop_exceptionproperty +_check_errors()call instream_events()finally blocktests/test_cancel_streaming.py— adds two tests:test_run_loop_exception_property_is_none_on_success— property isNoneon a clean runtest_run_loop_exception_surfaced_after_stream— exception is both raised throughstream_events()and accessible via the propertyTest plan
uv run pytest tests/test_cancel_streaming.py -v— 11 passed (9 existing + 2 new)uv run pyright src/agents/result.py— 0 errors