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

  1. Read controller.byobRequest fresh at the top of every pull() and respond at most once per non-null request.
  2. Never store the request beyond the current pull's lifetime.
  3. 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

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


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/646b6ab07fa6ef45. Report an issue: GitHub.