denoland/deno · error · TypeError
"bytesWritten" must be 0 when calling respond() on a closed
Error message
"bytesWritten" must be 0 when calling respond() on a closed stream: received ${bytesWritten} What it means
ReadableStreamBYOBRequest.respond() was called with a non-zero bytesWritten while the stream is in the 'closed' state. Once a byte stream is closed there is no pending consumer buffer left to fill, so the spec only permits respond(0) as a no-op acknowledgment. In practice this means code responded after controller.close() already ran.
Source
Thrown at ext/web/06_streams.js:2593
}
}
controller[_pendingPullIntos].enqueue(pullIntoDescriptor);
readableStreamAddReadIntoRequest(stream, readIntoRequest);
readableByteStreamControllerCallPullIfNeeded(controller);
}
/**
* @param {ReadableByteStreamController} controller
* @param {number} bytesWritten
* @returns {void}
*/
function readableByteStreamControllerRespond(controller, bytesWritten) {
assert(controller[_pendingPullIntos].size !== 0);
const firstDescriptor = controller[_pendingPullIntos].peek();
const state = controller[_stream][_state];
if (state === "closed") {
if (bytesWritten !== 0) {
throw new TypeError(
`"bytesWritten" must be 0 when calling respond() on a closed stream: received ${bytesWritten}`,
);
}
} else {
assert(state === "readable");
if (bytesWritten === 0) {
throw new TypeError(
'"bytesWritten" must be greater than 0 when calling respond() on a readable stream',
);
}
if (
(firstDescriptor.bytesFilled + bytesWritten) >
// deno-lint-ignore deno-internal/prefer-primordials
firstDescriptor.byteLength
) {
throw new RangeError('"bytesWritten" out of range');
}
}View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Check for zero-byte/EOF reads and call controller.close() instead of responding; never respond with bytes after close.
- Fetch controller.byobRequest fresh inside each pull() and respond exactly once, only while it is non-null.
- Sequence I/O: await the pending read before closing the stream.
- On a possibly-closed stream, respond(0) or skip — never with a byte count.
Example fix
// before
const done = bytesWritten === 0;
if (done) controller.close();
byob.respond(bytesWritten); // done && bytesWritten > 0 -> TypeError
// after
const done = bytesWritten === 0;
if (done) { controller.close(); return; } // EOF: close, do not respond
byob.respond(bytesWritten); Defensive patterns
Strategy: validation
Validate before calling
// helper: respond safely on a possibly-closed byte stream
function respondSafe(controller, n) {
const byob = controller.byobRequest;
if (byob === null) return; // no live request (closed or none pending)
if (n === 0) return; // nothing written: not a response
byob.respond(Math.min(n, byob.view.byteLength));
} Try / catch
try {
byob.respond(n);
} catch (e) {
if (e instanceof TypeError && e.message.includes('closed stream')) {
return; // stream already closed: nothing left to deliver
}
throw e;
} Prevention
- Respond at most once per pull(), in the same tick as the read that filled the view.
- Await pending I/O before calling controller.close().
- Never cache byobRequest across pulls — read controller.byobRequest fresh each time.
When it happens
Trigger: controller.close() executes and then a completion callback (OS read, socket event, promise chain) still calls byobRequest.respond(n > 0); responding from a stale byobRequest captured in an earlier pull(); an error path that closes the stream and then falls through to the success path that responds.
Common situations: File/socket adapters where EOF-triggered close races an in-flight read completion; async write callbacks resolving after close; source code holding a request object across pulls.
Related errors
- "bytesWritten" must be greater than 0 when calling respond()
- "bytesWritten" out of range
- The view's length must be 0 when calling respondWithNewView(
- The view's length must be greater than 0 when calling respon
- This BYOB request has been invalidated
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/d602ad63a769151f.
Report an issue: GitHub.