From cec4e33bc28551907ca3d2a06da67b07297666c6 Mon Sep 17 00:00:00 2001 From: endlessdev Date: Tue, 25 Aug 2026 22:49:33 +0900 Subject: [PATCH] fix(next-auth): detect Node responses by a callable headers.append MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `"headers" in response` is used to tell a Web `Response` from a Node `ServerResponse` before appending `set-cookie`. Node's `ServerResponse` has no `headers` property, so the check works there — but it is not portable. Bun's `node:http` `ServerResponse` *does* expose `headers`, and it is a plain object rather than a `Headers` instance. The check therefore takes the Web branch and throws: TypeError: response.headers.append is not a function Every session-reading Pages Router API route (`auth(req, res)`, `getServerSideProps`) returns a 500 as a result. The process still boots and health checks still pass, so this fails silently in production. Require a callable `append` instead. Real `Response` objects keep the `Headers` path; every `ServerResponse` falls through to `appendHeader`, which Node and Bun both implement. --- packages/next-auth/src/lib/index.ts | 30 ++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/next-auth/src/lib/index.ts b/packages/next-auth/src/lib/index.ts index 25503d2c55..da8aaf017f 100644 --- a/packages/next-auth/src/lib/index.ts +++ b/packages/next-auth/src/lib/index.ts @@ -188,9 +188,7 @@ export function initAuth( const auth = await parseSessionResponse(authResponse) for (const cookie of authResponse.headers.getSetCookie()) - if ("headers" in response) - response.headers.append("set-cookie", cookie) - else response.appendHeader("set-cookie", cookie) + appendSetCookie(response, cookie) return auth } @@ -238,14 +236,36 @@ export function initAuth( const auth = await parseSessionResponse(authResponse) for (const cookie of authResponse.headers.getSetCookie()) - if ("headers" in response) response.headers.append("set-cookie", cookie) - else response.appendHeader("set-cookie", cookie) + appendSetCookie(response, cookie) return auth }) } } +/** + * Append a `set-cookie` header to either a Web `Response` or a Node + * `ServerResponse` (Pages Router API routes, `getServerSideProps`). + * + * `"headers" in response` alone is not a reliable discriminator. Node's + * `ServerResponse` has no `headers` property, but some runtimes' Node + * compatibility layers do expose one. Bun, for example, exposes a plain object + * rather than a `Headers` instance, so the check takes the Web branch and then + * throws `TypeError: response.headers.append is not a function`, turning every + * session-reading Pages Router API route into a 500. + * + * Requiring a callable `append` keeps real Web `Response` objects on the + * `Headers` path and lets every `ServerResponse` fall through to + * `appendHeader`, which both runtimes implement. + */ +function appendSetCookie(response: any, cookie: string) { + if ("headers" in response && typeof response.headers?.append === "function") { + response.headers.append("set-cookie", cookie) + } else { + response.appendHeader("set-cookie", cookie) + } +} + async function handleAuth( args: Parameters, config: NextAuthConfig,