Request with ReadableStream body causes 'expected non-null body source' assertion failure in undici 7.x
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 7.7k
- Forks
- 880
- Avg merge
- 2d 16h
- Merged PRs (30d)
- 68
Description
Environment
- Node.js: v24.14.0 (undici 7.21.0)
- Platform: macOS 26.4 (Apple Silicon)
Description
In undici 7.x, creating a Request with a ReadableStream body causes an assertion failure:
Error: expected non-null body source
This is a breaking change from undici 6.x (Node 22 and earlier) where this worked fine.
Reproduction
const body = new TextEncoder().encode(JSON.stringify({ test: true }));
// Create original Request with Uint8Array body
const originalReq = new Request('https://example.com', {
method: 'POST',
body,
headers: { 'Content-Type': 'application/json' }
});
// Read the body as ReadableStream
const bodyStream = originalReq.body;
// Create new Request from ReadableStream (common pattern in middleware/proxies)
const newReq = new Request(originalReq.url, {
method: originalReq.method,
headers: originalReq.headers,
body: bodyStream,
duplex: 'half',
});
// Try to fetch with the new request
await fetch(newReq);
// → Error: expected non-null body source
Root Cause
In undici/lib/web/fetch/index.js, there's an assertion:
if (request.body != null) {
assert(request.body.source != null)
request.body = safelyExtractBody(request.body.source)[0]
}
When a Request is created with a ReadableStream body (instead of Uint8Array, string, etc.), the body.source property is null, causing the assertion to fail.
Impact
This breaks legitimate use cases:
- Middleware that re-creates requests (e.g., Next.js's fetch patching, proxies)
- Libraries that create intermediate
Requestobjects (e.g.,@atproto-labs/fetch-nodefor SSRF protection)
Expected Behavior
Creating a Request with a ReadableStream body should work, as it did in undici 6.x (Node 22).
Workaround
Downgrade to Node.js 22 (undici 6.x) or pre-read the body as ArrayBuffer before creating the new Request.
Question
Is this strict assertion intentional? If so, what's the recommended pattern for middleware that needs to re-create Request objects?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the provided Request and ReadableStream reproduction, then inspect the assertion in lib/web/fetch/index.js and the Request/fetch flow it enters. Compare the stream case with the Uint8Array case and determine whether the assertion is intentional. Done means the behavior or recommended middleware pattern is established, with regression coverage if a code change is warranted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100