From baba447095d694bd85fad953fad6df6f4f3d8460 Mon Sep 17 00:00:00 2001 From: mashfromband Date: Sun, 6 Sep 2026 13:09:15 +0900 Subject: [PATCH] fix(server): opt the compact route out of the request idle timeout `/v1/responses/compact` buffers the whole upstream turn before it answers, so the server-level `idleTimeout` (255 s) closes the client connection under a long remote compact even though the upstream is still working; the client then sees a truncated body ("error decoding response body" in Codex). The other buffered data-plane routes already call `disableResponsesRequestTimeout(req, requestServer)`; the compact route now does the same, and a source contract test pins the call inside that branch. Claude-Session: https://claude.ai/code/session_01VcTuv1wGXdywDr8xeQftdP --- src/server/index.ts | 5 +++++ tests/server/server-auth.test.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/server/index.ts b/src/server/index.ts index b51156abe7..c8c67bd6ff 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1752,6 +1752,11 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server { })).toBe(false); }); + test("compact route opts out of the request timeout before it buffers the compaction", () => { + // `/v1/responses/compact` answers only after the whole upstream turn has been collected, + // so unlike the streaming route it never sends a byte that would keep the connection + // alive. Without this opt-out the server-level `idleTimeout` closes a long remote + // compact mid-flight and the client sees a truncated body, not an error. + const source = readFileSync(new URL("../../src/server/index.ts", import.meta.url), "utf8"); + const compactStart = source.indexOf('url.pathname === "/v1/responses/compact" && req.method === "POST"'); + expect(compactStart).toBeGreaterThan(-1); + const handlerCall = source.indexOf("await handleResponsesCompact(req, config, logCtx", compactStart); + expect(handlerCall).toBeGreaterThan(compactStart); + expect(source.slice(compactStart, handlerCall)).toContain("disableResponsesRequestTimeout(req, requestServer);"); + }); + test("responses handler keeps the request timeout until the body is fully accepted", async () => { let controller!: ReadableStreamDefaultController; const body = new ReadableStream({