Practical guide

“TypeError: Body is unusable: Body has already been read” in Next.js and Node

We read a request body twice in a Next.js 16.4 route and a response twice in three browsers. Node says “Body is unusable”, Chrome “body stream already read”, Firefox “already been consumed”, Safari “disturbed or locked”.

The short answer. Body is unusable means the code read a request or response body that was already read. A body is a stream and can be consumed only once, by json(), text() or formData(). Read it once, keep the result in a variable and pass that on. If two readers really need it, call clone() before the first read.

FormgongUpdated

“TypeError: Body is unusable”: body has already been read
“TypeError: Body is unusable: Body has already been read” in Next.js and Node

What we saw

On 16.10.2026 we wrote a Next.js 16.4.2 route that reads await request.json(), logs a field, and then reads request.json() again, the way a logging helper or a validation library might. The first read worked. The second threw TypeError: Body is unusable: Body has already been read, and the route answered 500 with an empty body. In the browser that empty 500 then broke response.json() with “Unexpected end of JSON input”, so the visitor's side shows a different error from the server's.

The same mistake on a response in the browser gave a different message in each engine:

Reading a body twice
WhereMessage
Node 22 and Next.js route handlersTypeError: Body is unusable: Body has already been read
Chromium 152TypeError: Failed to execute 'json' on 'Response': body stream already read
Firefox 142TypeError: Response.json: Body has already been consumed.
WebKit 26 (Safari)TypeError: Body is disturbed or locked
Cloudflare Workers (workerd)TypeError: Body has already been used. It can only be used once. Use tee() first if you need to read it twice.

We check the Workers row with a test in this site's own code: it reads a body twice in the Workers runtime and compares the error with the row.

Why a body can be read only once

A request or response body is a stream. json(), text(), formData() and arrayBuffer() each read the stream to the end, and after that nothing is left to read. It often happens without a second json() in sight: middleware or an auth helper reads the body first, a console.log(await response.text()) added for debugging sits above the real json(), or a retry reuses the same Response.

Read once, or clone first

Keep the parsed body in a variable and pass it to whatever needs it. If two parts of the code really need the raw stream, call clone() before the first read and give each reader its own copy. The tee() in the Cloudflare message splits the body stream in two, and clone() calls it for you.

JavaScript (route handler)
export async function POST(request) {
  const body = await request.json(); // read once
  await logRequest(body);            // pass the parsed value on
  await saveMessage(body);
  return Response.json({ ok: true });
}

// If two places really need the raw body, clone before the first read:
// const copy = request.clone(); const forLog = await copy.text();

If the route only forwards a contact form

A route that exists only to pass form fields on to email can be dropped: the form can post straight to Formgong at https://formgong.com/submit, and there is no body of yours to read twice. See the HTML guide.

Frequently asked questions

What does TypeError: Body is unusable mean?

The code tried to read a request or response body that had already been read. A body is a stream and can be read only once.

How do I read a request body twice?

Call request.clone() before the first read and read the copy, or read the body once and pass the parsed value to the code that needs it.

What is the browser version of this error?

Chrome: body stream already read. Firefox: Body has already been consumed. Safari: Body is disturbed or locked.

What does Body has already been used. Use tee() first mean in Cloudflare Workers?

It is the Workers wording of the same error: the code read a request or response body that was already read. Read it once and pass the value on, or call clone() before the first read.

Sources and documentation

Official references for this guide: MDN: Request.clone(), MDN: Response.bodyUsed, Next.js: Route handlers. HTML contact form, where data is stored.

Rate this guide

Your rating

We read every rating. A comment appears here only after we approve it.

← Back to the blog