denoland/deno · error · TypeError
Target WritableStream is already locked
Error message
Target WritableStream is already locked
What it means
pipeThrough(transform, options) found transform.writable already locked — another writer or an active pipeTo owns it. pipeThrough must write into the transform's writable side, which requires acquiring it exclusively, so isWritableStreamLocked(writable) === true is rejected.
Source
Thrown at ext/web/06_streams.js:6223
const prefix = "Failed to execute 'pipeThrough' on 'ReadableStream'";
webidl.requiredArguments(arguments.length, 1, prefix);
transform = webidl.converters.ReadableWritablePair(
transform,
prefix,
"Argument 1",
);
options = webidl.converters.StreamPipeOptions(
options,
prefix,
"Argument 2",
);
const { readable, writable } = transform;
const { preventClose, preventAbort, preventCancel, signal } = options;
if (isReadableStreamLocked(this)) {
throw new TypeError("ReadableStream is already locked");
}
if (isWritableStreamLocked(writable)) {
throw new TypeError("Target WritableStream is already locked");
}
const promise = readableStreamPipeTo(
this,
writable,
preventClose,
preventAbort,
preventCancel,
signal,
);
setPromiseIsHandledToTrue(promise);
return readable;
}
/**
* @param {WritableStream<R>} destination
* @param {PipeOptions=} options
* @returns {Promise<void>}
*/View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Release any writer on transform.writable (writer.releaseLock()) before piping.
- Give each source its own TransformStream instance; create N transforms for N sources.
- Await or cancel the previous pipe before reusing a transform.
Example fix
// before
const ts = new TransformStream({});
const w = ts.writable.getWriter();
await w.write(header);
source.pipeThrough(ts); // TypeError: Target WritableStream is already locked
// after
const ts = new TransformStream({});
const w = ts.writable.getWriter();
await w.write(header);
w.releaseLock();
source.pipeThrough(ts); // ok Defensive patterns
Strategy: validation
Validate before calling
if (transform.writable.locked) {
throw new Error('transform writable locked — release its writer before piping');
}
source.pipeThrough(transform); Try / catch
try {
source.pipeThrough(transform);
} catch (e) {
if (e instanceof TypeError && e.message === 'Target WritableStream is already locked') {
headerWriter.releaseLock();
source.pipeThrough(transform); // retry once unlocked
} else throw e;
} Prevention
- A TransformStream's writable side takes one feeder at a time.
- Release header-prefill writers before piping.
- Create a fresh TransformStream per request/connection instead of sharing one.
When it happens
Trigger: transform.writable.getWriter() held (not released) when pipeThrough runs; the same TransformStream already fed by another pipeTo/pipeThrough; calling pipeThrough twice through one transform object.
Common situations: Reusing a single TransformStream across multiple sources; a wrapper pre-writing a header via getWriter() before handing the transform to pipeThrough; concurrent pipeline setup racing on a shared transform.
Related errors
- ReadableStream is already locked
- ReadableStream is locked
- ReadableStream is locked or disturbed
- Body is unusable
- ReadableStream is locked
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/ce2ca958d9ad2705.
Report an issue: GitHub.