denoland/deno · error · TypeError

"bytesWritten" must be greater than 0 when calling respond()

Error message

"bytesWritten" must be greater than 0 when calling respond() on a readable stream

What it means

ReadableStreamBYOBRequest.respond() was called with bytesWritten = 0 while the stream is still 'readable'. A zero-byte respond cannot satisfy the pending BYOB read, so the spec requires at least one byte; 'no bytes available' must instead be expressed by closing the stream (or by returning a pending promise from pull()).

Source

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

/**
 * @param {ReadableByteStreamController} controller
 * @param {number} bytesWritten
 * @returns {void}
 */
function readableByteStreamControllerRespond(controller, bytesWritten) {
  assert(controller[_pendingPullIntos].size !== 0);
  const firstDescriptor = controller[_pendingPullIntos].peek();
  const state = controller[_stream][_state];
  if (state === "closed") {
    if (bytesWritten !== 0) {
      throw new TypeError(
        `"bytesWritten" must be 0 when calling respond() on a closed stream: received ${bytesWritten}`,
      );
    }
  } else {
    assert(state === "readable");
    if (bytesWritten === 0) {
      throw new TypeError(
        '"bytesWritten" must be greater than 0 when calling respond() on a readable stream',
      );
    }
    if (
      (firstDescriptor.bytesFilled + bytesWritten) >
        // deno-lint-ignore deno-internal/prefer-primordials
        firstDescriptor.byteLength
    ) {
      throw new RangeError('"bytesWritten" out of range');
    }
  }
  firstDescriptor.buffer = ArrayBufferPrototypeTransferToFixedLength(
    // deno-lint-ignore deno-internal/prefer-primordials
    firstDescriptor.buffer,
  );
  readableByteStreamControllerRespondInternal(controller, bytesWritten);
}

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Treat bytesRead === 0 as EOF: call controller.close() and return without responding.
  2. If the operation failed, call controller.error(err) instead of respond(0).
  3. Only call respond(n) after actually writing n >= 1 bytes into byobRequest.view.

Example fix

// before
const { bytesRead } = await read(fd, byob.view);
byob.respond(bytesRead); // bytesRead === 0 -> TypeError: must be greater than 0

// after
const { bytesRead } = await read(fd, byob.view);
if (bytesRead === 0) { controller.close(); return; } // EOF
byob.respond(bytesRead);
Defensive patterns

Strategy: validation

Validate before calling

if (bytesRead === 0) { controller.close(); return; } // EOF, not respond(0)
if (controller.byobRequest !== null) controller.byobRequest.respond(bytesRead);

Try / catch

try {
  byob.respond(n);
} catch (e) {
  if (e instanceof TypeError && e.message.includes('must be greater than 0')) {
    controller.close(); // zero bytes on a readable stream means EOF
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: An OS read into byobRequest.view returns 0 bytes (EOF) and the adapter calls byob.respond(0) instead of controller.close(); an error path that 'responds with nothing' rather than calling controller.error(err).

Common situations: Adapting POSIX read()/file descriptors or sockets to a type: 'bytes' stream; TCP reads returning 0 on peer disconnect; loops copied from default-reader code where signaling emptiness is legal.

Related errors


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