diff --git a/.changeset/fix-miniflare-post-401.md b/.changeset/fix-miniflare-post-401.md new file mode 100644 index 0000000000..d15ab91c9f --- /dev/null +++ b/.changeset/fix-miniflare-post-401.md @@ -0,0 +1,7 @@ +--- +"miniflare": patch +--- + +fix(miniflare): prevent fetch failed on POST 401 responses + +Set `credentials: "omit"` in miniflare's fetch wrapper to bypass undici's HTTP 401 authentication retry logic. Workerd uses streamed request bodies where `body.source` is null, causing undici to throw "expected non-null body source" on POST 401 responses. diff --git a/packages/miniflare/src/http/fetch.ts b/packages/miniflare/src/http/fetch.ts index 8343f26a20..08df48222c 100644 --- a/packages/miniflare/src/http/fetch.ts +++ b/packages/miniflare/src/http/fetch.ts @@ -83,6 +83,15 @@ export async function fetch( const response = await undici.fetch(request, { dispatcher: requestInit?.dispatcher, + // Prevent undici's HTTP 401 authentication retry logic from triggering. + // When a POST request with a streamed body (body.source === null) receives + // a 401, undici tries to re-extract the request body for re-sending with + // credentials, but fails with "expected non-null body source" because + // workerd uses streamed bodies. Setting credentials to "omit" bypasses + // the 401 handling entirely (Fetch spec §4.4 step 14). This is safe + // because miniflare dispatches to workerd directly — there is no HTTP + // authentication to retry. + credentials: "omit", }); return new Response(response.body, response); } diff --git a/packages/miniflare/src/index.ts b/packages/miniflare/src/index.ts index 76cf0c9c2b..5da4c0030d 100644 --- a/packages/miniflare/src/index.ts +++ b/packages/miniflare/src/index.ts @@ -2707,6 +2707,14 @@ export class Miniflare { const forwardInit = forward as RequestInit; forwardInit.dispatcher = dispatcher; + // Omit credentials to prevent undici's HTTP 401 authentication retry + // logic from triggering. When a POST request with a streamed body + // receives a 401, undici tries to re-extract the body source for + // credential re-authentication. Since workerd uses streamed bodies + // (body.source === null), this causes a "fetch failed" network error + // with "expected non-null body source". Setting credentials to "omit" + // bypasses the 401 handling entirely (see Fetch spec step 14). + forwardInit.credentials = "omit"; const response = await fetch(url, forwardInit); // If the Worker threw an uncaught exception, propagate it to the caller