The stream is not in a state that permits enqueue; new Response(readable).blob().text() resolve to [object ReadableStream] not underlying source
See original GitHub issueConsider this code:
Promise.allSettled([
new Promise(async (resolve, reject) => {
let controller;
const rs = new ReadableStream(
{
start(_) {
return (controller = _);
},
},
{ highWaterMark: 1 }
);
for (let chunk of Array.from({ length: 100 }, (_, i) => i)) {
try {
controller.enqueue(new Uint8Array([chunk]));
} catch (err) {
console.warn(
`new Response().arrayBuffer() outside event handler: ${err.message}`
);
reject(err);
}
}
controller.close();
const buffer = await new Response(rs).arrayBuffer();
resolve(buffer);
}),
new Promise(async (resolve, reject) => {
let controller;
const rs = new ReadableStream(
{
start(_) {
return (controller = _);
},
},
{ highWaterMark: 1 }
);
const ac = new AudioContext();
if (ac.state === 'suspended') {
await ac.resume();
}
const msd = new MediaStreamAudioDestinationNode(ac);
const osc = new OscillatorNode(ac);
osc.connect(msd);
osc.onended = () => {
recorder.requestData();
recorder.stop();
};
const recorder = new MediaRecorder(msd.stream, {
audioBitrateMode: 'constant',
});
recorder.onstop = async (e) => {
// console.log(e.type);
const buffer = await new Response(rs).arrayBuffer();
resolve(buffer);
};
recorder.ondataavailable = async (e) => {
// console.log(e.type, e.data.size);
if (e.data.size > 0) {
try {
controller.enqueue(new Uint8Array(await e.data.arrayBuffer()));
} catch (err) {
console.warn(`Response.arrayBuffer(): ${err.message}`);
reject(err);
}
} else {
controller.close();
await ac.close();
}
};
osc.start(ac.currentTime);
recorder.start(1);
osc.stop(ac.currentTime + 1.15);
}),
new Promise(async (resolve, reject) => {
let controller;
const rs = new ReadableStream(
{
start(_) {
return (controller = _);
},
},
{ highWaterMark: 1 }
);
const ac = new AudioContext();
if (ac.state === 'suspended') {
await ac.resume();
}
const msd = new MediaStreamAudioDestinationNode(ac);
const osc = new OscillatorNode(ac);
osc.connect(msd);
osc.onended = () => {
recorder.requestData();
recorder.stop();
};
const recorder = new MediaRecorder(msd.stream, {
audioBitrateMode: 'constant',
});
recorder.onstop = async (e) => {
// console.log(e.type);
const buffer = await new Response(rs).blob();
resolve(buffer);
};
recorder.ondataavailable = async (e) => {
// console.log(e.type, e.data.size);
if (e.data.size > 0) {
try {
controller.enqueue(new Uint8Array(await e.data.arrayBuffer()));
} catch (err) {
console.warn(`Response.blob(): ${err.message}`);
reject(err);
}
} else {
controller.close();
await ac.close();
}
};
osc.start(ac.currentTime);
recorder.start(1);
osc.stop(ac.currentTime + 1.15);
}),
])
.then(async (data) => {
console.table(data);
console.log(await data[2].value.text());
})
.catch((err) => console.error(err));
Using Streams API shipped with Chromium 96.0.4651.0 (Developer Build) (64-bit) Revision aabda36a688c0883019e7762faa00db6342a7e37-refs/heads/main@{#924134} this error is thrown
TypeError: Failed to execute 'enqueue' on 'ReadableStreamDefaultController': Cannot enqueue a chunk into a readable stream that is closed or has been requested to be closed
This polyfill, included as
<script src="https://unpkg.com/web-streams-polyfill/dist/polyfill.min.js"></script>
at https://plnkr.co/edit/XwtpbXt4aqTQTIhb?preview throws
The stream is not in a state that permits enqueue
Observe that the first element of the array passed to Promise.allSettled() uses a basic loop in which ReadableStreamDefaultController.enqueue() is called with a Uint8Array() as value, then close() is called, then the ReadableStream is passed to new Response() and read to completion using blob().
The second element of the array follows the same pattern as the first element, except close() being called in MediaRecorder dataavailable event handler, and new Response(readable).arrayBuffer() called in onstop event handler. The TypeError is still thrown, logged at console, yet not caught in try..catch.
The third element of the array follows same pattern as first and second element, though uses Response.blob(), which always throws the TypeError.
I do not see anywhere in the code where enqueue() is called after close().
Using the polyfill I did observe where the third element fulfilled, was not rejected, while the very next test with the same code blob() throws an error.
The Streams API shipped with Chromium always throws https://bugs.chromium.org/p/chromium/issues/detail?id=1253143.
The polyfill does not resolve to the underlying data enqueued when Response(readable).blob(), rather appears to resolve to a string
[object ReadableStream]
There should not be any error thrown. The sequence is close(), then new Response(readable) with arrayBuffer() or blob() chained.
This behaviour of both Chromium Streams API and this polyfill makes no sense to me. (This cannot be working as intended.) Kindly illuminate.
Issue Analytics
- State:
- Created 2 years ago
- Comments:10 (3 by maintainers)

Top Related StackOverflow Question
Thank you, again for your analysis and solution. I will re-read the code until I gather exactly what is occuring, though I am now reminded of
promises.push(new Promise(resolve => {}))in synchronousforloop thenPromise.all(promises).Not that one, the one with
blob.size = 733. The firstondataavailablecall isn’t “done” yet after step 2, it lasts all the way until step 5. Meanwhile, anotherondataavailableevent is fired in step 3 withblob.size = 0. That’s what’s causing the seemingly “out of order” execution.The key insight is to realize that the event loop does not await the result of your
ondataavailablehandler, so it’ll happily call it again as soon as the synchronous call is done.It’s still technically true though: the polyfill faithfully implements the entire Streams API. What it doesn’t do is implement all other web APIs that integrate with the Streams API, such as
fetch.I do see this sort of question coming up more frequently, so I’ll try to explain this better in the README.
I mean, that’s probably even easier? You can pass other
Blobs to theBlobconstructor, and then callconcatenatedBlob.arrayBuffer()at the end. If you’re not interested in the concrete byte data of any individual chunk, you don’t really need to turn each chunk into its ownUint8Arrayfirst.