Summary
Follow-up to #1628 (fixed by #1632, which handled a finite generator streamed to completion). A distinct code path on the same generator→SSE-terminal surface is still broken: a streaming Resource whose async generator throws partway through iteration over SSE (Accept: text/event-stream) logs an uncaughtException and the HTTP response never closes on its own — the client hangs indefinitely.
Repro / observation (harper main 31de6a3be)
A ThrowGen streaming Resource that yields 2 events then throws, consumed over SSE:
- Node logs an
uncaughtException originating from the internal nextAsync / Readable.from machinery.
- The response is never terminated by the server. In the test harness only a 15s
AbortController ended it (events=2, ended=false, aborted=true).
For contrast, in the same run every completing generator (empty / single / 5-event / 3000-event) closed cleanly with the exact event count and no uncaughtException — i.e. the #1628 fix is solid; this is purely the error path.
Exploratory-QA repro fixture is available (a ThrowGen Resource alongside FiniteGen/EmptyGen/SingleGen/LargeGen), and can be turned into a regression test once the fix lands.
Impact
- Any app whose SSE-streamed generator can throw mid-iteration (a DB error mid-scan, an upstream/origin failure, or a bug in user generator code) leaves that client connection wedged open, plus an
uncaughtException in the logs (noise + potential worker instability — a post-run liveness probe suggested the worker itself survives via a global handler, but the erroring request does not terminate).
Suggested direction
The SSE terminal/teardown path needs to treat a generator rejection the same way it now treats terminal {done:true} — surface the error to the response (or at least close the stream) rather than letting it escape as an uncaughtException with the connection left open.
Surfaced by the exploratory-QA loop while regression-verifying #1628. 🤖 Issue drafted by an LLM (KrAIs / Claude Opus 4.8); please sanity-check the root-cause framing before prioritizing.
Summary
Follow-up to #1628 (fixed by #1632, which handled a finite generator streamed to completion). A distinct code path on the same generator→SSE-terminal surface is still broken: a streaming Resource whose async generator throws partway through iteration over SSE (
Accept: text/event-stream) logs anuncaughtExceptionand the HTTP response never closes on its own — the client hangs indefinitely.Repro / observation (harper main
31de6a3be)A
ThrowGenstreaming Resource that yields 2 events then throws, consumed over SSE:uncaughtExceptionoriginating from the internalnextAsync/Readable.frommachinery.AbortControllerended it (events=2, ended=false, aborted=true).For contrast, in the same run every completing generator (empty / single / 5-event / 3000-event) closed cleanly with the exact event count and no
uncaughtException— i.e. the #1628 fix is solid; this is purely the error path.Exploratory-QA repro fixture is available (a
ThrowGenResource alongsideFiniteGen/EmptyGen/SingleGen/LargeGen), and can be turned into a regression test once the fix lands.Impact
uncaughtExceptionin the logs (noise + potential worker instability — a post-run liveness probe suggested the worker itself survives via a global handler, but the erroring request does not terminate).Suggested direction
The SSE terminal/teardown path needs to treat a generator rejection the same way it now treats terminal
{done:true}— surface the error to the response (or at least close the stream) rather than letting it escape as anuncaughtExceptionwith the connection left open.Surfaced by the exploratory-QA loop while regression-verifying #1628. 🤖 Issue drafted by an LLM (KrAIs / Claude Opus 4.8); please sanity-check the root-cause framing before prioritizing.