denoland/deno · error · TypeError
The BYOB request's buffer has been detached and so cannot be
Error message
The BYOB request's buffer has been detached and so cannot be used as a response
What it means
Thrown by ReadableStreamBYOBRequest.respond(bytesWritten) in Deno's WHATWG Streams implementation (ext/web/06_streams.js). Before forwarding bytesWritten to the stream controller, respond() re-reads the view it holds, extracts its ArrayBuffer, and checks isDetachedBuffer(buffer); if the buffer was detached (ownership moved elsewhere), the response cannot be written and a TypeError aborts the call. This matches the spec requirement that the respond target still be valid memory at response time.
Source
Thrown at ext/web/06_streams.js:6738
{
enforceRange: true,
},
);
if (this[_controller] === undefined) {
throw new TypeError("This BYOB request has been invalidated");
}
let buffer, byteLength;
if (isTypedArray(this[_view])) {
buffer = TypedArrayPrototypeGetBuffer(this[_view]);
byteLength = TypedArrayPrototypeGetByteLength(this[_view]);
} else {
buffer = DataViewPrototypeGetBuffer(this[_view]);
byteLength = DataViewPrototypeGetByteLength(this[_view]);
}
if (isDetachedBuffer(buffer)) {
throw new TypeError(
"The BYOB request's buffer has been detached and so cannot be used as a response",
);
}
assert(byteLength > 0);
assert(getArrayBufferByteLength(buffer) > 0);
readableByteStreamControllerRespond(this[_controller], bytesWritten);
}
respondWithNewView(view) {
webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype);
const prefix =
"Failed to execute 'respondWithNewView' on 'ReadableStreamBYOBRequest'";
webidl.requiredArguments(arguments.length, 1, prefix);
view = webidl.converters.ArrayBufferView(view, prefix, "Argument 1");
if (this[_controller] === undefined) {
throw new TypeError("This BYOB request has been invalidated");
}View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Do not transfer or detach the buffer between obtaining controller.byobRequest and calling respond(); keep ownership until respond completes.
- If ownership must move, copy the bytes into a fresh Uint8Array and call byobRequest.respondWithNewView(freshView) instead of respond().
- Audit every postMessage/structuredClone transfer list for the ArrayBuffer backing the BYOB view.
- Always read the current view from byobRequest.view at the moment of use instead of caching a view or buffer from an earlier tick.
Example fix
// before const view = byobRequest.view; view.buffer.transfer(); // move memory elsewhere -> buffer detached byobRequest.respond(bytesWritten); // TypeError: buffer detached // after const view = byobRequest.view; writeInto(view); byobRequest.respond(bytesWritten); // buffer still attached
Defensive patterns
Strategy: validation
Validate before calling
// Before respond(): a detached ArrayBuffer reports byteLength 0,
// and (ES2024) exposes `detached === true`.
const view = byobRequest.view;
const buf = view && (view.buffer ?? null);
if (buf === null || buf.byteLength === 0 || buf.detached === true) {
controller.close(); // or recover by enqueuing from a fresh buffer
} else {
byobRequest.respond(bytesWritten);
} Type guard
function isRespondableView(req) {
const v = req.view;
return v != null && v.byteLength > 0 && v.buffer.byteLength > 0;
} Try / catch
try {
byobRequest.respond(n);
} catch (err) {
if (err instanceof TypeError && /detached/.test(err.message)) {
// buffer moved: recover with a fresh, owned view
byobRequest.respondWithNewView(new Uint8Array(ownedCopy));
} else throw err;
} Prevention
- Never list the BYOB view's buffer in a postMessage/structuredClone transfer list before respond() has run.
- Call respond() synchronously in the same tick you obtain controller.byobRequest.
- Prefer respondWithNewView with a buffer you provably own when building zero-copy pipelines.
When it happens
Trigger: Calling byobRequest.respond(n) after detaching the view's buffer: view.buffer.transfer() (ArrayBuffer.prototype.transfer), postMessage(view, [view.buffer]) with the buffer in a transfer list, or structuredClone(buffer, [buffer]). Typical in fetch handlers or Deno.serve response code that serves a body from a BYOB view and moves that memory before responding.
Common situations: Zero-copy pipelines that use ArrayBuffer.prototype.transfer() to hand data to a Worker and then try to respond with the same view; transfer lists that accidentally include the BYOB buffer; caching a view across an await during which another task transfers it.
Related errors
- The given view's buffer has been detached and so cannot be u
- Chunk's buffer is detached and so cannot be enqueued
- The BYOB request's buffer has been detached and so cannot be
- The buffer of view has different capacity than byobRequest
- Closed already requested.
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/acd6b29e9e9cd28e.
Report an issue: GitHub.