denoland/deno · error · TypeError
This BYOB request has been invalidated
Error message
This BYOB request has been invalidated
What it means
respond() (or respondWithNewView()) was called on a ReadableStreamBYOBRequest whose internal controller reference is undefined — the request was invalidated. The controller issues a new BYOBRequest per pending pull-into and invalidates the old one whenever the pending read is fulfilled or shifted (e.g. by a controller.enqueue()), the stream closes, or the request is superseded. Responding on any stale request object throws.
Source
Thrown at ext/web/06_streams.js:6726
}
this[_brand] = _brand;
}
respond(bytesWritten) {
webidl.assertBranded(this, ReadableStreamBYOBRequestPrototype);
const prefix = "Failed to execute 'respond' on 'ReadableStreamBYOBRequest'";
webidl.requiredArguments(arguments.length, 1, prefix);
bytesWritten = webidl.converters["unsigned long long"](
bytesWritten,
prefix,
"Argument 1",
{
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);View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Read controller.byobRequest fresh at the top of every pull() and respond at most once per non-null request.
- Never store the request beyond the current pull's lifetime.
- If events can arrive late, re-check controller.byobRequest before responding and drop responses for superseded requests.
Example fix
// before
class Source {
pull(controller) {
if (!this.byob) this.byob = controller.byobRequest; // cached once
this.byob.respond(n); // later pull -> TypeError: invalidated
}
}
// after
class Source {
pull(controller) {
const byob = controller.byobRequest; // fresh every pull
if (byob === null) return; // none pending
byob.respond(n);
}
} Defensive patterns
Strategy: validation
Validate before calling
// always re-derive; the old object is dead after any enqueue/respond/close const byob = controller.byobRequest; if (byob === null) return; // request invalidated or none pending byob.respond(n);
Try / catch
try {
byob.respond(n);
} catch (e) {
if (e instanceof TypeError && e.message.includes('invalidated')) {
return; // superseded request — ignore the late response
}
throw e;
} Prevention
- Treat controller.byobRequest as single-use and pull-scoped.
- Exactly one respond/respondWithNewView per request object, ever.
- After any enqueue() or close(), previous requests are dead — re-fetch before responding.
When it happens
Trigger: Caching controller.byobRequest in a variable (or on this) and reusing it across pulls; calling respond() twice on the same request; responding on a request that a parallel controller.enqueue() already satisfied; responding after close().
Common situations: Source adapters storing byobRequest on the instance for convenience; event-driven code where a late event responds on a superseded request; refactors from per-pull requests to a cached-request design.
Related errors
- "bytesWritten" must be 0 when calling respond() on a closed
- "bytesWritten" must be greater than 0 when calling respond()
- "bytesWritten" out of range
- The BYOB request's buffer has been detached and so cannot be
- The view's length must be 0 when calling respondWithNewView(
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/646b6ab07fa6ef45.
Report an issue: GitHub.