denoland/deno · error · TypeError

Cannot use a BYOB reader with a non-byte stream

Error message

Cannot use a BYOB reader with a non-byte stream

What it means

A BYOB reader was attached to a ReadableStream that is not a byte stream: its controller is a ReadableStreamDefaultController because it was constructed without { type: 'bytes' }, so it fails the ObjectPrototypeIsPrototypeOf(ReadableByteStreamControllerPrototype, ...) check. read-into (BYOB) semantics only exist for byte streams, whose controller can fill caller-provided buffers.

Source

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

  );
}

/**
 * @template R
 * @param {ReadableStreamBYOBReader} reader
 * @param {ReadableStream<R>} stream
 */
function setUpReadableStreamBYOBReader(reader, stream) {
  if (isReadableStreamLocked(stream)) {
    throw new TypeError("ReadableStream is locked");
  }
  if (
    !(ObjectPrototypeIsPrototypeOf(
      ReadableByteStreamControllerPrototype,
      stream[_controller],
    ))
  ) {
    throw new TypeError("Cannot use a BYOB reader with a non-byte stream");
  }
  readableStreamReaderGenericInitialize(reader, stream);
  reader[_readIntoRequests] = new Queue();
}

/**
 * @template R
 * @param {ReadableStreamDefaultReader<R>} reader
 * @param {ReadableStream<R>} stream
 */
function setUpReadableStreamDefaultReader(reader, stream) {
  if (isReadableStreamLocked(stream)) {
    throw new TypeError("ReadableStream is locked");
  }
  readableStreamReaderGenericInitialize(reader, stream);
  reader[_readRequests] = new Queue();
}

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Create the source as a byte stream: new ReadableStream({ type: 'bytes', ... }).
  2. Use a default reader (getReader()) when the stream is a value stream.
  3. If you control the pipeline, funnel value-stream chunks into a byte stream you construct yourself.

Example fix

// before
const stream = new ReadableStream({ start(c) { c.enqueue(new Uint8Array(1)); } });
const reader = stream.getReader({ mode: 'byob' }); // TypeError: non-byte stream

// after
const stream = new ReadableStream({ type: 'bytes', start(c) { c.enqueue(new Uint8Array(1)); } });
const reader = stream.getReader({ mode: 'byob' }); // ok
Defensive patterns

Strategy: type-guard

Type guard

function isByteStream(s) {
  if (s.locked) return false; // cannot probe while locked
  try {
    const r = s.getReader({ mode: 'byob' });
    r.releaseLock();
    return true;
  } catch {
    return false; // not a byte stream
  }
}
const reader = isByteStream(stream)
  ? stream.getReader({ mode: 'byob' })
  : stream.getReader();

Try / catch

try {
  reader = stream.getReader({ mode: 'byob' });
} catch (e) {
  if (e instanceof TypeError && e.message.includes('non-byte stream')) {
    reader = stream.getReader(); // fall back to a default reader
  } else throw e;
}

Prevention

When it happens

Trigger: stream.getReader({ mode: 'byob' }) or new ReadableStreamBYOBReader(stream) on a stream created via new ReadableStream() without type: 'bytes'; applying a BYOB reader to the readable side of a TransformStream; applying it to framework-produced value streams (fetch bodies, helpers).

Common situations: Generic utility functions that assume all streams are byte streams; adapting a value stream and attempting zero-copy reads; library code that must work on both kinds and probes BYOB support by trying it.

Related errors


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