Skip to content

SSE: streaming a finite async generator to completion hangs the response + throws uncaughtException (terminal-step serialize) #1628

Description

@kriszyp

Summary

Any custom Resource that streams a plain async generator to natural completion over SSE (Accept: text/event-stream) hangs the HTTP response and throws a process uncaughtException the instant the generator returns. Data events + [DONE] are delivered, but the response never closes. This sits directly on the LLM/openaiStream completion pattern #631 is building toward, and on any bounded/export-over-SSE stream.

Root cause (code-confirmed)

server/serverHelpers/contentTypes.tstransformIterable's next() unconditionally calls transform(step.value) even on the generator's terminal step { value: undefined, done: true }. SSE serialize() (contentTypes.ts:135) opens with if (message.acknowledge) message.acknowledge(), but message is undefined on that step → undefined.acknowledge throws:

TypeError: Cannot read properties of undefined (reading 'acknowledge')
    at serialize (server/serverHelpers/contentTypes.ts:135)
    ← contentTypes.ts:627
    ← nextAsync (node:internal/streams/from:182)

Value-shape-independent: an AckShapeGen whose yielded items carry a real .acknowledge() succeeds 5/5 on the items — only the terminal undefined crashes.

Why it wasn't hit before

The common infinite-subscription SSE case (IterableEventQueue) never reaches a terminal done step. A plain generator returned over Accept: application/json closes cleanly (control) — the bug is confined to the SSE serialize/transformIterable path on finite generators.

Repro

A minimal static async *connect() yielding a few plain JSON objects then returning, requested with Accept: text/event-stream, hangs (response end never fires) + logs the uncaughtException. The same generator as application/json closes cleanly. Harper 228eacc0f.

Fix

Guard transformIterable to not transform the terminal done step (if (!step.done)), and/or null-guard serialize(message).

Found via exploratory QA. Related but distinct from #1399 (SSE teardown deferral).

— KrAIs 🤖 (exploratory QA, on Kris's behalf)

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Fields

Priority

P1

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions