workerd TypeError: TransformStream default writer already released
Fixes the TransformStream error thrown when code writes to a stream writer after releaseLock or acquires a second writer on the same stream. This skill shows how to find the double-acquire, sequence writes before close, and keep a single live writer. Use it when streaming proxy or transform code in a Worker throws that the default writer was already released.
Fix TransformStream default writer already released
TL;DR
Never write to a writer after calling releaseLock() - either finish all writes first or re-acquire with getWriter() after closing. The usual bug is calling getWriter() twice on one stream or writing after releaseLock(). Find the double-acquire, await writer.close() before any second acquire, and keep exactly one live writer per stream.
Verbatim error
workerd TypeError: TransformStream default writer already releasedSteps
- Find every acquire and release: run
grep -rn "getWriter" src/andgrep -rn "releaseLock" src/. Expected: a short list of call sites, usually two acquires on the same stream or a write after a release. - Restructure so only one writer exists at a time: do all writes, then
await writer.close(), and only then callgetWriter()again if you truly need another. Expected: no secondgetWriter()while a writer is open. - Move
releaseLock()to after the final write and theawait writer.close(). Expected: writes complete before the lock is released. - If you use
tee(), write only through the two branch streams and never touch the original stream's writer again. Expected: all writes go to branch writers. - Redeploy and exercise the streaming path while watching
wrangler tail. Expected: the stream completes with no TypeError.
Use this when
- A Worker throws "TransformStream default writer already released"
- Custom TransformStream code proxies or transforms streaming responses
- The error appears after adding a second writer or a
releaseLock()call
Not for this skill when
- The error is about a ReadableStream reader being released (mirror issue, different fix)
- You see "Response body already used" (single-use body problem)
pipeToaborts with an AbortError (abort handling, not writer lifecycle)
Variant phrasings
- Cannot write to a released writer TransformStream
- getWriter called twice on transform stream
- workerd transform stream writer lock released
- TypeError: writer has been released
Why it happens
A stream has a single default writer lock. releaseLock() frees the lock but leaves the writer object dead - any later write() on it throws. Calling getWriter() a second time while the first writer still holds the lock throws the same error. Both are lifecycle bugs, not runtime bugs.
Edge cases
- Aborting via an AbortController can release the writer implicitly; guard writes after aborts.
tee()closes branches independently - closing one branch does not close the other.- Middleware that also acquires a writer on a stream you pass through will collide with your writer; pass the readable side onward instead.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_7US5gjRPQ6z19sqNwS8Eyw
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.