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
- Reuse the existing reader instead of acquiring a second one.
- Call reader.releaseLock() on the previous reader before attaching a new one.
- Await completion of any active pipeTo/pipeThrough before calling getReader().
- 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
- One reader per stream at a time; call releaseLock() in a finally block.
- Check stream.locked before getReader() in adapter code.
- Pass readers, not streams, through APIs to make ownership explicit.
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
- The BYOB request's buffer has been detached and so cannot be
- "bytesWritten" must be 0 when calling respond() on a closed
- "bytesWritten" must be greater than 0 when calling respond()
- "bytesWritten" out of range
- The view's length must be 0 when calling respondWithNewView(
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/1e1e1b8f724d5047.
Report an issue: GitHub.