{"record":{"id":"ce2ca958d9ad2705","repo":"denoland/deno","slug":"target-writablestream-is-already-locked","errorCode":null,"errorMessage":"Target WritableStream is already locked","messagePattern":"Target WritableStream is already locked","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/web/06_streams.js","lineNumber":6223,"sourceCode":"    const prefix = \"Failed to execute 'pipeThrough' on 'ReadableStream'\";\n    webidl.requiredArguments(arguments.length, 1, prefix);\n    transform = webidl.converters.ReadableWritablePair(\n      transform,\n      prefix,\n      \"Argument 1\",\n    );\n    options = webidl.converters.StreamPipeOptions(\n      options,\n      prefix,\n      \"Argument 2\",\n    );\n    const { readable, writable } = transform;\n    const { preventClose, preventAbort, preventCancel, signal } = options;\n    if (isReadableStreamLocked(this)) {\n      throw new TypeError(\"ReadableStream is already locked\");\n    }\n    if (isWritableStreamLocked(writable)) {\n      throw new TypeError(\"Target WritableStream is already locked\");\n    }\n    const promise = readableStreamPipeTo(\n      this,\n      writable,\n      preventClose,\n      preventAbort,\n      preventCancel,\n      signal,\n    );\n    setPromiseIsHandledToTrue(promise);\n    return readable;\n  }\n\n  /**\n   * @param {WritableStream<R>} destination\n   * @param {PipeOptions=} options\n   * @returns {Promise<void>}\n   */","sourceCodeStart":6205,"sourceCodeEnd":6241,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/web/06_streams.js#L6205-L6241","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst ts = new TransformStream({});\nconst w = ts.writable.getWriter();\nawait w.write(header);\nsource.pipeThrough(ts); // TypeError: Target WritableStream is already locked\n\n// after\nconst ts = new TransformStream({});\nconst w = ts.writable.getWriter();\nawait w.write(header);\nw.releaseLock();\nsource.pipeThrough(ts); // ok","handlingStrategy":"validation","validationCode":"if (transform.writable.locked) {\n  throw new Error('transform writable locked — release its writer before piping');\n}\nsource.pipeThrough(transform);","typeGuard":null,"tryCatchPattern":"try {\n  source.pipeThrough(transform);\n} catch (e) {\n  if (e instanceof TypeError && e.message === 'Target WritableStream is already locked') {\n    headerWriter.releaseLock();\n    source.pipeThrough(transform); // retry once unlocked\n  } else throw e;\n}","preventionTips":["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."],"tags":["readable-stream","pipethrough","transform-stream","locked-stream"],"backgroundTag":"writable-stream-locked","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}