Skip to content

Request with ReadableStream body causes 'expected non-null body source' assertion failure in undici 7.x #5004

@satyam-mishra-pce

Description

@satyam-mishra-pce

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 Request objects (e.g., @atproto-labs/fetch-node for 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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions