denoland/deno · error · TypeError

ReadableStream is locked

Error message

ReadableStream is locked

What it means

A ReadableStreamBYOBReader (new ReadableStreamBYOBReader(stream) or stream.getReader({ mode: 'byob' })) was attached to a stream that is already locked. ReadableStream allows exactly one active reader or one active pipe at a time; stream.locked === true means a reader, getReader(), or pipeTo/pipeThrough currently owns it.

Source

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

  setUpReadableStreamDefaultController(
    stream,
    controller,
    startAlgorithm,
    pullAlgorithm,
    cancelAlgorithm,
    highWaterMark,
    sizeAlgorithm,
  );
}

/**
 * @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
 */

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Reuse the existing reader instead of acquiring a second one.
  2. Call reader.releaseLock() on the previous reader before attaching a new one.
  3. Await completion of any active pipeTo/pipeThrough before calling getReader().
  4. Use stream.tee() when two independent consumers must read the same data.

Example fix

// before
const r1 = stream.getReader();
const r2 = stream.getReader({ mode: 'byob' }); // TypeError: ReadableStream is locked

// after
const r1 = stream.getReader();
// ... finish reading ...
r1.releaseLock();
const r2 = stream.getReader({ mode: 'byob' }); // ok
Defensive patterns

Strategy: validation

Validate before calling

if (stream.locked) {
  throw new Error('stream already has a reader or active pipe — release it first');
}
const reader = stream.getReader({ mode: 'byob' });

Try / catch

try {
  reader = stream.getReader({ mode: 'byob' });
} catch (e) {
  if (e instanceof TypeError && e.message === 'ReadableStream is locked') {
    previousReader.releaseLock(); // release the known prior holder, then retry once
    reader = stream.getReader({ mode: 'byob' });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling getReader() earlier and then getReader({ mode: 'byob' }); a pipeTo()/pipeThrough() still running on the stream while acquiring a reader; a previous reader whose releaseLock() was never called.

Common situations: Wrapping a stream that user code already reads; middleware that takes a stream and internally calls getReader(); inspection/tee wrappers that acquire a reader and forget to release it.

Related errors


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