denoland/deno · error · TypeError
The stream is already locked
Error message
The stream is already locked
What it means
WritableStreamDefaultWriter (stream.getWriter() or new WritableStreamDefaultWriter(stream)) was acquired while the writable was already locked. A WritableStream supports one writer or one active pipe at a time; isWritableStreamLocked(stream) === true triggers this TypeError.
Source
Thrown at ext/web/06_streams.js:4790
stream,
controller,
startAlgorithm,
writeAlgorithm,
closeAlgorithm,
abortAlgorithm,
highWaterMark,
sizeAlgorithm,
);
}
/**
* @template W
* @param {WritableStreamDefaultWriter<W>} writer
* @param {WritableStream<W>} stream
*/
function setUpWritableStreamDefaultWriter(writer, stream) {
if (isWritableStreamLocked(stream) === true) {
throw new TypeError("The stream is already locked");
}
writer[_stream] = stream;
stream[_writer] = writer;
const state = stream[_state];
if (state === "writable") {
if (
writableStreamCloseQueuedOrInFlight(stream) === false &&
stream[_backpressure] === true
) {
writer[_readyPromise] = new Deferred();
} else {
writer[_readyPromise] = new Deferred();
writer[_readyPromise].resolve(undefined);
}
writer[_closedPromise] = new Deferred();
} else if (state === "erroring") {
writer[_readyPromise] = new Deferred();
writer[_readyPromise].reject(stream[_storedError]);View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Call writer.releaseLock() when done — including in finally blocks after errors.
- Keep a single long-lived writer per stream instead of acquiring one per write.
- Await pipeTo() completion before acquiring a writer; abort the pipe first if you must preempt.
- Route concurrent producers through one queue owned by a single writer.
Example fix
// before
const w1 = ws.getWriter();
await w1.write('a');
const w2 = ws.getWriter(); // TypeError: The stream is already locked
// after
const w1 = ws.getWriter();
await w1.write('a');
w1.releaseLock();
const w2 = ws.getWriter(); // ok Defensive patterns
Strategy: validation
Validate before calling
if (ws.locked) {
throw new Error('writable is locked — release the writer or await the pipe');
}
const writer = ws.getWriter(); Try / catch
try {
writer = ws.getWriter();
} catch (e) {
if (e instanceof TypeError && e.message === 'The stream is already locked') {
previousWriter.releaseLock(); // then retry once
writer = ws.getWriter();
} else throw e;
} Prevention
- One writer per stream; call releaseLock() in finally, including after abort()/close().
- Await the writer's close()/abort() before creating another.
- Pass writers, not writables, across API boundaries to encode ownership.
When it happens
Trigger: Two getWriter() calls without an intervening releaseLock(); pipeTo() still writing while code calls getWriter(); a writer abandoned after abort()/close() without releaseLock(); wrappers that internally getWriter() on a stream the caller also writes to.
Common situations: Shared WritableStreams (logs, response sinks) written by several producers; helpers that pre-write a header via getWriter() before handing off; retry logic re-acquiring a writer per attempt.
Related errors
- A writable stream is not associated with the writer
- Cannot collect output: 'stdout' is locked
- Cannot collect output: 'stderr' is locked
- ReadableStream is locked
- ReadableStream is already locked
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/3ab9abeaa8ff6cc4.
Report an issue: GitHub.