You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
extractBody: a byte-sequence body is enqueued again on each pull until the stream closes (since 8.12.0) #6092
Since #5926 (8.12.0), extractBody enqueues a string or byte body in pull(). Then it closes the stream in a queueMicrotask callback. If the stream calls pull() again before that callback runs, pull() enqueues the full body again.
With the real queueMicrotask, the close runs first, so the body is correct. When queueMicrotask is delayed, the reader gets the same bytes again and again. Jest fake timers fake queueMicrotask by default. In our Jest suite, MSW requests then read an infinite body, and the test process stops with "JavaScript heap out of memory".
In 8.11.2, the same setup waits for the close and does not repeat data.
Reproducible by
// npm i undici@8.12.3 @sinonjs/fake-timers@15constFakeTimers=require("@sinonjs/fake-timers");const{ Response }=require("undici");asyncfunctionmain(){constclock=FakeTimers.install({toFake: ["queueMicrotask"]});constreader=newResponse("hello").body.getReader();letchunks=0;while(chunks<5){const{ done, value }=awaitreader.read();if(done)break;chunks++;console.log(newTextDecoder().decode(value));}clock.uninstall();console.log(`chunks read: ${chunks}`);}main();
Expected behavior
The reader gets hello one time. The stream does not repeat the body, even if the close is late.
Logs & Screenshots
8.12.3 prints hello five times, then chunks read: 5. Without the limit, the loop does not stop. 8.11.2 prints hello one time.
I'd like to take this one. thinking pull() should only enqueue the body once (track that it was already sent) and close right away instead of depending on the queueMicrotask timing, plus a test with a delayed queueMicrotask.
Activity
cestercian commentedon Oct 26, 2026
I'd like to take this one. thinking pull() should only enqueue the body once (track that it was already sent) and close right away instead of depending on the queueMicrotask timing, plus a test with a delayed queueMicrotask.