GeezerVison is showing github.com — fix(harness): close the turn's event source when delivery stops early by michaelxu2288 · Pull Request #541 · scaleapi/scale-agentex-python
Conversation
michaelxu2288
commented
Oct 1, 2026
•
edited by greptile-apps
Bot
Loading
Uh oh!
There was an error while loading. Please reload this page.
Problem
The harness delivery adapters never close the event source they iterate:
auto_send, the async push path behind UnifiedEmitter.auto_send_turn;
yield_events and UnifiedEmitter.yield_turn, the sync HTTP yield path.
Their finally blocks close streaming contexts and flush span derivation, but leave turn.events alone. Every CLI tap depends on being closed. The Claude Code and Codex taps close their raw line iterator in a finally, and that close is what terminates the CLI subprocess in the scaffolds.
It goes wrong in two cases:
- a turn is cancelled (an interrupt, or an activity cancel) while delivery is awaiting the backend (
ctx.stream_update or a context close) rather than the source;
- a sync client disconnects mid-stream.
In both, the source stays suspended at a yield. The turn object keeps a reference to its event generator, and agents hold the turn to read turn.session_id. So garbage collection never finalizes it, and the CLI subprocess and its stdout pipe leak.
Fix
auto_send and yield_events close events in their finally, after the existing cleanup. The close is guarded with contextlib.suppress(Exception), so a failing aclose cannot mask the original exception.
UnifiedEmitter.yield_turn closes its delivery generator explicitly instead of leaving it to async-generator finalization.
- Plain async iterators without
aclose, and generators that are already exhausted, behave exactly as before.
Verification
- New tests:
test_auto_send.py: cancel auto_send while stream_update blocks, with the source held by a local and no gc.collect();
test_yield_delivery.py: close yield_events early;
test_emitter.py: close yield_turn early, with a turn that pins its generator the way the CLI taps do.
- All three fail on
main with assert [] == [True] (the source's finally never ran) and pass here.
- An extra test checks that a source without
aclose still delivers normally.
uv run pytest -n 0 tests/lib/core/harness tests/lib/adk: 571 passed, 1 skipped.
ruff check and pyright (1.1.399) are clean.
Confidence Score: 5/5
The PR appears safe to merge; no actionable issue remains established.
Summary
Harness delivery now closes event sources when delivery stops early, so CLI tap cleanup runs on cancellation and client disconnects. The ACP response stream also closes its handler generator, carrying disconnect cleanup upstream.
- Async push delivery closes its source after its existing cleanup.
- Sync harness delivery passes early closure through to the event source.
- ACP response streaming closes the handler generator when the stream ends.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[ACP response body closes] --> B[Handler generator closes]
B --> C[yield_turn closes delivery]
C --> D[yield_events closes turn events]
Loading
Reviews (2) · Last reviewed commit: "fix(acp): close the handler generator wh..."
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
greptile-apps[bot] reacted with thumbs up emoji
The delivery adapters (auto_send for the async push path, yield_events
and UnifiedEmitter.yield_turn for the sync yield path) closed their
streaming contexts and flushed span derivation in `finally`, but never
closed the event source they were iterating. Every tap relies on that
close: the Claude Code and Codex taps close their raw line iterator in a
`finally`, which is what terminates the CLI subprocess in the scaffolds.
When a turn is cancelled while delivery is awaiting the backend
(stream_update or a context close) rather than the source, or a sync
client disconnects mid-stream, the source stays suspended at a yield.
The turn object keeps a reference to its event generator, so garbage
collection does not finalize it while the caller still holds the turn,
and the CLI subprocess and its stdout handle leak.
Both adapters now close the source in `finally`, after the existing
cleanup and guarded so a failing aclose cannot mask the original
exception. yield_turn closes its delivery generator explicitly instead
of leaving it to async-generator finalization. Plain async iterators
without aclose and already-exhausted generators are unaffected.
Three new tests (cancel during a blocked stream_update; early close of
yield_events; early close of yield_turn with a turn that pins its
generator) fail on main with `assert [] == [True]` and pass here.
tests/lib/core/harness and tests/lib/adk: 571 passed, 1 skipped.
Uh oh!
There was an error while loading. Please reload this page.
_handle_streaming_response iterated the handler's async generator inside
generate_json_rpc_stream but never closed it. When a client disconnects,
Starlette closes the body iterator, and the handler (with the harness
turn and CLI subprocess under it) stayed suspended until garbage
collection. Close the handler generator in a finally, guarded so a
failing aclose cannot mask the original error.
New test: take one chunk from the streaming response, close the body
iterator, and assert the handler generator's finally ran. It fails on
main (assert [] == [True]) and passes here.
This branch has not been deployed
No deployments
Reviewers
At least 1 approving review is required to merge this pull request.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
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.
|