denoland/deno · error · RangeError

"bytesWritten" out of range

Error message

"bytesWritten" out of range

What it means

RangeError from ReadableStreamBYOBRequest.respond(bytesWritten): firstDescriptor.bytesFilled + bytesWritten exceeds firstDescriptor.byteLength, meaning you reported more bytes than fit in the capacity handed out in byobRequest.view. The descriptor tracks how much of the consumer's view is already filled; over-reporting would claim memory beyond the view.

Source

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

  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);
}

/**
 * @param {ReadableByteStreamController} controller
 * @param {number} bytesWritten
 * @param {PullIntoDescriptor} pullIntoDescriptor
 * @returns {void}
 */
function readableByteStreamControllerRespondInReadableState(
  controller,
  bytesWritten,

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Respond with this read call's count only: byob.respond(bytesReadThisCall).
  2. Clamp before responding: bytesWritten = Math.min(bytesWritten, byob.view.byteLength).
  3. When coalescing partial writes, keep a local fill counter and respond with only what fits; leave the rest for the next pull().

Example fix

// before
let total = 0;
for (const part of parts) { total += part.length; copyInto(byob.view, part); }
byob.respond(total); // exceeds view capacity -> RangeError

// after
let filled = 0;
for (const part of parts) {
  if (filled + part.length > byob.view.byteLength) break;
  copyInto(byob.view.subarray(filled), part);
  filled += part.length;
}
byob.respond(filled);
Defensive patterns

Strategy: validation

Validate before calling

const cap = byob.view.byteLength;        // capacity handed out
const n = Math.min(bytesWritten, cap); // never over-report
if (n > 0) byob.respond(n);
else controller.close();

Try / catch

try {
  byob.respond(n);
} catch (e) {
  if (e instanceof RangeError && e.message.includes('out of range')) {
    byob.respond(byob.view.byteLength); // retry with the valid maximum
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing bytesWritten greater than byobRequest.view.byteLength; summing bytes across several partial reads into one respond(); confusing units (bits vs bytes, total file offset vs chunk length); ignoring that an earlier partial fill already consumed part of the view.

Common situations: Read loops that accumulate totals and respond once; porting Node 'bytesWritten' semantics; responding with a payload length plus header size that exceeds the view.

Related errors


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