From bbe5c8866bda5f0a379a585ace5acfea6840c55d Mon Sep 17 00:00:00 2001 From: chenchen Date: Mon, 18 May 2026 18:33:47 +0800 Subject: [PATCH] fix: increase proxy stream timeout from 30s to 10min The 30-second AbortSignal.timeout on streaming fetch requests was causing long-running tool calls (file editing, code search) to be silently killed. The UI would show the task as still running but no data was flowing, making it appear stuck indefinitely. - Increase fetch timeout to 600s (10 min) for both stream and non-stream requests - Add 5-minute per-chunk timeout in both stream parsers so a truly dead upstream is detected and surfaced as an error instead of hanging forever Co-Authored-By: Claude Opus 4.6 --- cc-haha/src/server/proxy/handler.ts | 4 ++-- .../server/proxy/streaming/openaiChatStreamToAnthropic.ts | 8 +++++++- .../proxy/streaming/openaiResponsesStreamToAnthropic.ts | 8 +++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/cc-haha/src/server/proxy/handler.ts b/cc-haha/src/server/proxy/handler.ts index 88e0044..36bf7ba 100644 --- a/cc-haha/src/server/proxy/handler.ts +++ b/cc-haha/src/server/proxy/handler.ts @@ -119,7 +119,7 @@ async function handleOpenaiChat( Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify(transformed), - signal: isStream ? AbortSignal.timeout(30_000) : AbortSignal.timeout(300_000), + signal: AbortSignal.timeout(600_000), }) if (!upstream.ok) { @@ -176,7 +176,7 @@ async function handleOpenaiResponses( Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify(transformed), - signal: isStream ? AbortSignal.timeout(30_000) : AbortSignal.timeout(300_000), + signal: AbortSignal.timeout(600_000), }) if (!upstream.ok) { diff --git a/cc-haha/src/server/proxy/streaming/openaiChatStreamToAnthropic.ts b/cc-haha/src/server/proxy/streaming/openaiChatStreamToAnthropic.ts index 054ecc7..90743ee 100644 --- a/cc-haha/src/server/proxy/streaming/openaiChatStreamToAnthropic.ts +++ b/cc-haha/src/server/proxy/streaming/openaiChatStreamToAnthropic.ts @@ -105,7 +105,13 @@ export function openaiChatStreamToAnthropic( try { while (true) { - const { done, value } = await reader.read() + const readResult = await Promise.race([ + reader.read(), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Stream chunk timeout: no data for 5 minutes')), 300_000) + ), + ]) + const { done, value } = readResult if (done) break buffer += decoder.decode(value, { stream: true }) diff --git a/cc-haha/src/server/proxy/streaming/openaiResponsesStreamToAnthropic.ts b/cc-haha/src/server/proxy/streaming/openaiResponsesStreamToAnthropic.ts index 560fe05..7b131cc 100644 --- a/cc-haha/src/server/proxy/streaming/openaiResponsesStreamToAnthropic.ts +++ b/cc-haha/src/server/proxy/streaming/openaiResponsesStreamToAnthropic.ts @@ -44,7 +44,13 @@ export function openaiResponsesStreamToAnthropic( try { while (true) { - const { done, value } = await reader.read() + const readResult = await Promise.race([ + reader.read(), + new Promise((_, reject) => + setTimeout(() => reject(new Error('Stream chunk timeout: no data for 5 minutes')), 300_000) + ), + ]) + const { done, value } = readResult if (done) break buffer += decoder.decode(value, { stream: true })