denoland/deno · error · TypeError

Cannot enqueue chunk after a close has been requested

Error message

Cannot enqueue chunk after a close has been requested

What it means

Thrown by ReadableByteStreamController.enqueue(chunk) when this[_closeRequested] is true (ext/web/06_streams.js). After close() has been called, the stream has committed to ending and accepts no more chunks; enqueueing past that point is a producer contract violation and throws a TypeError.

Source

Thrown at ext/web/06_streams.js:6885

    }
    if (byteLength === 0) {
      throw webidl.makeException(
        TypeError,
        "Length must be non-zero",
        prefix,
        arg1,
      );
    }
    if (getArrayBufferByteLength(buffer) === 0) {
      throw webidl.makeException(
        TypeError,
        "Buffer length must be non-zero",
        prefix,
        arg1,
      );
    }
    if (this[_closeRequested] === true) {
      throw new TypeError(
        "Cannot enqueue chunk after a close has been requested",
      );
    }
    if (this[_stream][_state] !== "readable") {
      throw new TypeError(
        "Cannot enqueue chunk when underlying stream is not readable",
      );
    }
    return readableByteStreamControllerEnqueue(this, chunk);
  }

  /**
   * @param {any=} e
   * @returns {void}
   */
  error(e = undefined) {
    webidl.assertBranded(this, ReadableByteStreamControllerPrototype);
    if (e !== undefined) {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Enforce single-producer discipline: after any code path calls close(), no path may enqueue — gate both with a shared `closed`/`done` flag.
  2. Only close when the source is fully drained; buffer late chunks and decide close vs enqueue in one place.
  3. Check readiness before enqueueing: skip when the source signaled end or the flag says closed.

Example fix

// before
source.on("end", () => controller.close());
source.on("data", (d) => controller.enqueue(d)); // may fire after close

// after
let ended = false;
source.on("end", () => { ended = true; controller.close(); });
source.on("data", (d) => { if (!ended) controller.enqueue(d); });
Defensive patterns

Strategy: validation

Validate before calling

let done = false;
function push(controller, chunk) {
  if (done) return false; // closed: stop producing
  controller.enqueue(chunk);
  return true;
}
function finish(controller) {
  if (!done) { done = true; controller.close(); }
}

Prevention

When it happens

Trigger: A loop or async callback calling enqueue() after the code path already called close(); an EOF branch that closes while another racing producer callback still enqueues; closing on empty source then pushing late-arriving data.

Common situations: Event-driven producers (file watchers, sockets) that close on idle but receive events afterwards; multiple pending pull() invocations where one closes; cleanup that closes early while buffered writes are still flushed.

Related errors


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