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

  1. Call writer.releaseLock() when done — including in finally blocks after errors.
  2. Keep a single long-lived writer per stream instead of acquiring one per write.
  3. Await pipeTo() completion before acquiring a writer; abort the pipe first if you must preempt.
  4. 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

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


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