Latest Results
feat(fetch)!: throw when an event stream ends without a close event (#169)
`toAsyncIteratorObject` now only finishes cleanly on a `close` event, or
throws `ErrorEvent` on an `error` event. A stream that ends without
either now rejects with `EventStreamDecoderError('Event Stream ended
before complete')`. That is the same error a stream gets when it ends
partway through a message. The rule also covers a missing (`null`) body.
Since #125, `toEventStream` always sends a `close` event, so streams
from standard-server senders are unaffected. This change lets the client
actually use that signal: a dropped connection no longer looks like a
finished stream.
## Why
When the server's pipeline errors, the client already sees a network
error. Many cut-offs still reach the client as a clean end of stream,
though, and before this change they read as a finished stream. In a
local probe with Node's `fetch` and `http2` client:
- an HTTP/1.1 response ended early (`res.end()`)
- a connection-close-delimited body (an HTTP/1.0 hop) whose connection
drops
- an HTTP/2 stream ended early, or its session torn down
Proxy timeouts, serverless time limits and graceful shutdowns can end
responses in these ways.
## Scope
- Applies to `@standard-server/fetch`, `@standard-server/node` and
`@standard-server/aws-lambda`, which share the fetch implementation.
- `@standard-server/peer` is unchanged. It already rejects when its
queue closes before a `close` event.
- Stopping early with `return()` still resolves as done.
## Breaking change
- Reading an event stream that ends without a `close` event now throws
instead of resolving `{ done: true, value: undefined }`.
- This affects SSE senders that never send `event: close`: generic SSE
APIs, and standard-server senders that set the old
`emptyCloseEventEnabled: false` option.
- `toAsyncIteratorObject(null)` now throws on the first `next()`. This
happens, for example, with a HEAD or 204 response labelled
`text/event-stream`.
## Testing
- Updated the tests that relied on a close-less stream finishing
quietly. They now either send `event: close` or expect the error.
- Added an empty-stream case and changed the null-stream case to expect
the error.
- `pnpm run check` (sherif, oxlint, oxfmt, tsc) and the full vitest
suite pass locally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01NYpqRELEp4zdwCYWeqRMAj
---------
Co-authored-by: Claude <noreply@anthropic.com> Latest Branches
0%
claude/serene-wozniak-qotci6 0%
dependabot/npm_and_yarn/dev-dependencies-minor-patch-b2a3be9c91 0%
dependabot/npm_and_yarn/remix-run/node-fetch-server-1.0.0 © 2026 CodSpeed Technology