From 78491e921f9d9e43c0d5f69e680eea8ccffdafda Mon Sep 17 00:00:00 2001 From: Rob Orton Date: Mon, 13 Apr 2026 11:26:01 -0600 Subject: [PATCH] fix: move res.end() outside stream reading loop in setResponse res.end() was called inside the for loop after a successful res.write(), causing the response to be terminated after the first chunk. This truncates any response body larger than one chunk (~16KB), returning incomplete JSON to the client. This breaks applications with large response payloads (e.g. better-auth customSession with production user data exceeding 16KB). The fix moves res.end() after the loop so the full response body is streamed before ending. --- .../src/adapters/node/request.test.ts | 35 +++++++++++++++++++ .../better-call/src/adapters/node/request.ts | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/better-call/src/adapters/node/request.test.ts b/packages/better-call/src/adapters/node/request.test.ts index 2fd8571..6b8632c 100644 --- a/packages/better-call/src/adapters/node/request.test.ts +++ b/packages/better-call/src/adapters/node/request.test.ts @@ -385,6 +385,41 @@ describe("getRequest", () => { }); describe("setResponse", () => { + it("should write the full response body when it spans multiple chunks", async () => { + const socket = new Socket(); + const req = new IncomingMessage(socket); + const res = new ServerResponse(req); + + const chunks: Buffer[] = []; + let ended = false; + + res.writeHead = vi.fn().mockReturnValue(res); + res.write = vi.fn().mockImplementation((chunk: Buffer) => { + chunks.push(Buffer.from(chunk)); + return true; + }); + res.end = vi.fn().mockImplementation(() => { + ended = true; + return res; + }); + + // Create a response body larger than 16KB to ensure multiple chunks + const largePayload = JSON.stringify({ + data: "x".repeat(32 * 1024), + }); + + const webResponse = new Response(largePayload, { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + + await setResponse(res, webResponse); + + const fullBody = Buffer.concat(chunks).toString(); + expect(fullBody).toBe(largePayload); + expect(ended).toBe(true); + }); + it("should set res.statusCode before writeHead for middleware compatibility", async () => { // Regression test for https://github.com/better-auth/better-auth/issues/7035 // Some frameworks/middleware read res.statusCode before writeHead is called. diff --git a/packages/better-call/src/adapters/node/request.ts b/packages/better-call/src/adapters/node/request.ts index ea3edb5..5313198 100644 --- a/packages/better-call/src/adapters/node/request.ts +++ b/packages/better-call/src/adapters/node/request.ts @@ -330,8 +330,8 @@ export async function setResponse(res: ServerResponse, response: Response) { return; } } - res.end(); } + res.end(); } catch (error) { cancel(error instanceof Error ? error : new Error(String(error))); }