-
Notifications
You must be signed in to change notification settings - Fork 30.6k
Description
Link to the code that reproduces this issue
https://github.com/masakij/nextjs-expected-non-null-body-source-repro
To Reproduce
The reproduction is very simple. Execute this inside an App Router handler (e.g., Server Action or Route Handler) targeting an endpoint that returns a 4xx or 5xx error:
// 1. Create a Request object with a body
const req = new Request('https://httpstat.us/401', { // An endpoint that returns 401
method: 'POST',
body: JSON.stringify({ test: true }),
headers: { 'Content-Type': 'application/json' }
});
// 2. Pass the Request object directly to global fetch
// 💥 This crashes immediately on Node >= 24.14.0 when the response is an error
const res = await fetch(req);Current vs. Expected behavior
Expected Behavior:
The patched fetch should return the Response object with res.ok === false without crashing, just as it does in a pure Node.js environment.
Actual Behavior:
The application crashes with TypeError: expected non-null body source at ignore-listed frames.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 25.3.0: Wed Jan 28 20:53:31 PST 2026; root:xnu-12377.81.4~5/RELEASE_ARM64_T8122
Available memory (MB): 16384
Available CPU cores: 8
Binaries:
Node: 24.14.0
npm: 11.9.0
Yarn: N/A
pnpm: 10.29.3
Relevant Packages:
next: 16.1.6 // Latest available version is detected (16.1.6).
eslint-config-next: N/A
react: 19.2.4
react-dom: 19.2.4
typescript: 5.9.3
Next.js Config:
output: standaloneWhich area(s) are affected? (Select all that apply)
Not sure
Which stage(s) are affected? (Select all that apply)
next dev (local)
Additional context
Description:
In the App Router environment, using the global patched fetch crashes with TypeError: expected non-null body source under a very specific set of conditions:
You pass a Request object (that contains a body, e.g., POST/PUT) directly to fetch().
The request has Content-Type: application/json.
The server responds with an error status (e.g., 401 or 500).
This issue surfaces specifically on newer Node.js versions (>= v24.14.0 and probably later v25.x) where undici strictly enforces stream locking.
It appears that when Next.js receives a JSON error response, its internal patch-fetch logic attempts to read or clone the original Request object (likely for error logging, tracing, or cache evaluation). Because the stream has already been consumed by the actual network request, attempting to access it again throws this error.
This commonly affects users of wrapper libraries like openapi-fetch that construct a Request object internally and pass it to the global fetch.
Current Workarounds:
Until this is fixed in the framework, developers can use one of the following workarounds depending on their use case:
- Deconstruct the Request: Pass the URL and
RequestInitseparately instead of theRequestobject.
// ❌ Fails on error response
fetch(new Request(url, init));
// ✅ Works perfectly
fetch(url, init); -
Use Native Undici Fetch: Bypass the Next.js patched fetch completely for non-GET requests by importing the native fetch:
import { fetch as undiciFetch } from 'undici'; -
Downgrade Node.js: Use Node.js
<= v24.13.1where undici's stream handling was less strict. -
Change Content-Type: If the API allows, using a
Content-Typeother thanapplication/jsonseems to bypass the internal cloning logic causing the crash.
(Note: The investigation and debugging were done manually, but I used an AI assistant to help structure and translate this issue report into English.)