{"record":{"id":"acd6b29e9e9cd28e","repo":"denoland/deno","slug":"the-byob-request-s-buffer-has-been-detached-and-so-acd6b2","errorCode":null,"errorMessage":"The BYOB request's buffer has been detached and so cannot be used as a response","messagePattern":"The BYOB request's buffer has been detached and so cannot be used as a response","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/web/06_streams.js","lineNumber":6738,"sourceCode":"      {\n        enforceRange: true,\n      },\n    );\n\n    if (this[_controller] === undefined) {\n      throw new TypeError(\"This BYOB request has been invalidated\");\n    }\n\n    let buffer, byteLength;\n    if (isTypedArray(this[_view])) {\n      buffer = TypedArrayPrototypeGetBuffer(this[_view]);\n      byteLength = TypedArrayPrototypeGetByteLength(this[_view]);\n    } else {\n      buffer = DataViewPrototypeGetBuffer(this[_view]);\n      byteLength = DataViewPrototypeGetByteLength(this[_view]);\n    }\n    if (isDetachedBuffer(buffer)) {\n      throw new TypeError(\n        \"The BYOB request's buffer has been detached and so cannot be used as a response\",\n      );\n    }\n    assert(byteLength > 0);\n    assert(getArrayBufferByteLength(buffer) > 0);\n    readableByteStreamControllerRespond(this[_controller], bytesWritten);\n  }\n\n  respondWithNewView(view) {\n    webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype);\n    const prefix =\n      \"Failed to execute 'respondWithNewView' on 'ReadableStreamBYOBRequest'\";\n    webidl.requiredArguments(arguments.length, 1, prefix);\n    view = webidl.converters.ArrayBufferView(view, prefix, \"Argument 1\");\n\n    if (this[_controller] === undefined) {\n      throw new TypeError(\"This BYOB request has been invalidated\");\n    }","sourceCodeStart":6720,"sourceCodeEnd":6756,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/web/06_streams.js#L6720-L6756","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst view = byobRequest.view;\nview.buffer.transfer(); // move memory elsewhere -> buffer detached\nbyobRequest.respond(bytesWritten); // TypeError: buffer detached\n\n// after\nconst view = byobRequest.view;\nwriteInto(view);\nbyobRequest.respond(bytesWritten); // buffer still attached","handlingStrategy":"validation","validationCode":"// Before respond(): a detached ArrayBuffer reports byteLength 0,\n// and (ES2024) exposes `detached === true`.\nconst view = byobRequest.view;\nconst buf = view && (view.buffer ?? null);\nif (buf === null || buf.byteLength === 0 || buf.detached === true) {\n  controller.close(); // or recover by enqueuing from a fresh buffer\n} else {\n  byobRequest.respond(bytesWritten);\n}","typeGuard":"function isRespondableView(req) {\n  const v = req.view;\n  return v != null && v.byteLength > 0 && v.buffer.byteLength > 0;\n}","tryCatchPattern":"try {\n  byobRequest.respond(n);\n} catch (err) {\n  if (err instanceof TypeError && /detached/.test(err.message)) {\n    // buffer moved: recover with a fresh, owned view\n    byobRequest.respondWithNewView(new Uint8Array(ownedCopy));\n  } else throw err;\n}","preventionTips":["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."],"tags":["streams","byob","arraybuffer","detached-buffer","readable-stream"],"backgroundTag":"detached-arraybuffer","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}