vercel

resumable-stream

Stream resumption for web streams

by vercel

TypeScript

TypeScript96.3%
Shell3.7%

Commit activity

Last 12 weeks · 3 commits

Full graph →

Community health

2 of 6 standards met

Community profile →
50
✓README✓License○Contributing○Code of Conduct○Issue Template○PR Template

Recent PRs & issues

Active · 6 in progress · Last activity 1mo ago
See all on GitHub →
zirkelc

Version:** resumable-stream 2.2.12 (Node 24, client) Problem In (), the source is read with and there is no rejection handler. When the stream returned by errors, three things happen: 1. The rejected read is an unhandled promise rejection. With Node's default , this ends the process. 2. The sentinel key is only set to in the branch, so it stays for 24 h. The subscription is never removed, and listeners never receive . If the process survives, a resumer receives the chunks and then waits forever. If the process died, nobody answers on the request channel. 3. The passed to never resolves. The source is not the only way into this state. The handler itself awaits (one call for each listener and chunk) and, in the branch, for the sentinel and . If one of these rejects (for example, the Redis connection has a short failure while chunks go to a resumer), the promise from the handler rejects with no handler. The result is the same: an unhandled rejection, is not called again, and the sentinel stays . A consumer cannot guard this path by wrapping its source. After that, every sees the sentinel , publishes a request, gets no ack, and rejects with after 1 s, for the full 24 h TTL. Reproduction Suggested fix Handle the rejection of and of the handler (for example, add a to the chain). On error, do the same cleanup as on (set the sentinel to , unsubscribe from the request channel, send to the listeners, resolve ), and then call (or close it) for the stream that returns. Optionally, could resolve instead of rejecting when the ack times out, because a producer that does not answer has nothing to resume. Related #50 is about the resume side: the ack timer can finish without settling the promise. This issue is about the producer side, which leaves the orphan sentinel that #50 then waits on. #42 is about the fixed 1000 ms ack timeout. Workaround Wrap the source so that an error ends it cleanly before reads it.

zirkelc · 5d ago
FaBrCh

Summary In (, v2.2.12) the 1000 ms ack timer callback can finish without ever settling the promise returned to the caller, so hangs forever instead of rejecting with . Where Two paths never settle: 1. Strict clock comparison. routinely fires with . When and the sentinel complete within that same millisecond (loopback Redis, in-cluster Valkey), the guard is false, and neither nor runs. The timer already guarantees the budget, so the comparison is redundant and should just be unconditional. 2. Unhandled rejection inside the async callback. The callback is and unawaited. If or rejects (Redis error, permission denied, connection lost), the rejection escapes as an unhandled rejection and, again, nothing settles. A third, smaller issue: the resumed has no algorithm, so a consumer that cancels it leaves the handler attached until another message arrives. Reproduction of (1) Node 22, simulating the two sub-millisecond awaits: Against a real Redis, seeding an orphan sentinel () with no producer and stubbing to a constant makes hang deterministically; with the fix below it rejects after ~1 s as documented. Suggested fix plus on the (hoisting / out of ). We are running this as a local patch and can send a PR if you would like one.

FaBrCh · 2w ago
nikolailehbrink

Version:** resumable-stream 2.2.13 (redis 6.3.0, Upstash) Problem In , the producer's read loop awaits a publish to every rejoined listener before it reads the next chunk: Once a client rejoins with , the producer reads at most one chunk per Redis round trip. The source stream is usually a tee of an AI SDK UI message stream, so backpressure slows the whole pipeline: generation, persistence, and reacting to an abort signal. With about 55 ms round trip time (a laptop talking to a regional Upstash database), a rejoined reply streamed about 350 characters in 3 seconds instead of about 2000, and a stop requested meanwhile only took effect after the buffered backlog was drained. Stale listeners (#48, #49) make this worse, because every chunk then waits on publishes to channels nobody reads anymore. Expected Reading the source does not wait for publishes to listeners. One publisher connection already keeps publishes in order, so awaiting each publish is not needed for ordering. Suggested fix Publish without awaiting and handle rejections: We apply this as a patch on 2.2.13 and are happy to open a PR.

nikolailehbrink · 2w ago
ken8203

Summary Fixes a race condition where consumers could miss the producer's request channel subscription. Problem Previously, the sentinel key was set before subscribing to the request channel: Solution Move sentinel set to after the subscribe completes: Changes Moved from wrapper to inside the actual function, after completes Updated comments to explain the ordering requirement Testing All existing tests pass.

ken8203 · 1mo ago
mdnanocom

Summary add optional to default it to , validate it as a positive finite number, and thread it through the internal runtime context use the configured timeout for both the resume timer and the elapsed-time timeout check add tests covering the default timeout, a custom timeout, and invalid timeout values update the README and generated type documentation Why hardcoded a 1,000 ms listener acknowledgement timeout. That caused avoidable failures when Redis or pub-sub acknowledgement was delayed by transient latency. Closes #42. Validation

mdnanocom · 1mo ago
Structured data for AI agents

Repository: vercel/resumable-stream. Description: Stream resumption for web streams Stars: 581, Forks: 45. Primary language: TypeScript. Languages: TypeScript (96.3%), Shell (3.7%). License: MIT. Homepage: https://sdk.vercel.ai Open PRs: 6, open issues: 15. Last activity: 1mo ago. Community health: 50%. Top contributors: cramforce, ethshea, mattpocock, MaxLeiter, lgrammel, MrLoh, ken8203, matanper, mdnanocom, 634750802.