Skip to content

templates/worker-tinygo: make /echo work under wrangler dev - #204

Merged
syumai merged 1 commit into
syumai:mainfrom
shibukawa:fix-tinygo-template-echo
Aug 21, 2026
Merged

templates/worker-tinygo: make /echo work under wrangler dev#204
syumai merged 1 commit into
syumai:mainfrom
shibukawa:fix-tinygo-template-echo

Conversation

@shibukawa

Copy link
Copy Markdown
Contributor

Summary

The worker-tinygo template's /echo endpoint fails under wrangler dev with:

TypeError: Body has already been used. It can only be used once. Use tee() first if you need to read it twice.

This PR fixes the template so it works out of the box, and documents why in a comment. Fixes #176.

Root cause

io.Copy(w, req.Body) triggers readableStreamToReadCloser.WriteTo's fast path in internal/jsutil/stream.go, which passes the request's raw ReadableStream through to the JS Response object via WriteRawJSBody.

This zero-copy passthrough is valid on production workerd, but miniflare routes every request through a proxy worker (miniflare/dist/src/workers/core/entry.worker.js), and that layer cannot relay a response whose body is the incoming request's own stream — await service.fetch(request) throws the error above.

This is an environment limitation, not a bug in this library. A plain JavaScript worker reproduces it under wrangler dev with no Go involved:

export default {
  async fetch(req) {
    return new Response(req.body); // same TypeError under wrangler dev
  },
};

(Related: cloudflare/workers-sdk#4373, another "streams work in production but not in miniflare" case.)

Since wrangler dev is the first thing every new user of the template runs, the template shouldn't rely on the passthrough. The worker-go template already buffers the body in /echo, which is why only the tinygo template was affected; this change aligns the two.

Testing

Verified with TinyGo 0.41.1 + wrangler 4.124.0 (and wrangler 3.109) on macOS:

$ curl -X POST -d "test message" http://localhost:8787/echo
# before: TypeError: Body has already been used. ... (HTTP 500)
# after:  test message

/hello unaffected.

io.Copy(w, req.Body) triggers readableStreamToReadCloser.WriteTo's
fast path, which hands the request's raw ReadableStream to the JS
Response object. miniflare's proxy worker cannot relay such a response
and fails with 'Body has already been used', so the template's /echo
endpoint breaks out of the box under wrangler dev (it works when
deployed to production workerd).

Buffer the body before writing, matching the worker-go template.

Fixes syumai#176

@syumai syumai left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@syumai
syumai merged commit 6a421fc into syumai:main Aug 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

workers-tinygo main.go /echo API: Body has already been used error

2 participants