From 0a41b8b2875c0ea80f838eb051caa33f31d5921e Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Sat, 4 Apr 2026 17:13:38 -0700 Subject: [PATCH] Add client retry logic --- .../[workspaceId]/home/hooks/use-chat.ts | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts index bcd0e0b402..2824383e9a 100644 --- a/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts +++ b/apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts @@ -108,7 +108,9 @@ const DEPLOY_TOOL_NAMES: Set = new Set([ ]) const RECONNECT_TAIL_ERROR = 'Live reconnect failed before the stream finished. The latest response may be incomplete.' -const RECOVERY_RETRY_DELAYS_MS = [250, 500, 1000, 2000] as const +const MAX_RECONNECT_ATTEMPTS = 10 +const RECONNECT_BASE_DELAY_MS = 1000 +const RECONNECT_MAX_DELAY_MS = 30_000 const logger = createLogger('useChat') @@ -1278,11 +1280,29 @@ export function useChat( setIsReconnecting(true) try { - for (let attempt = 0; attempt <= RECOVERY_RETRY_DELAYS_MS.length; attempt++) { + for (let attempt = 0; attempt <= MAX_RECONNECT_ATTEMPTS; attempt++) { if (abortController.signal.aborted || isStale()) { return { attached: false, hadStreamError: false, aborted: true } } + if (attempt > 0) { + const delayMs = Math.min( + RECONNECT_BASE_DELAY_MS * 2 ** (attempt - 1), + RECONNECT_MAX_DELAY_MS + ) + logger.warn('Reconnect attempt', { + streamId, + attempt, + maxAttempts: MAX_RECONNECT_ATTEMPTS, + delayMs, + }) + setIsReconnecting(true) + await waitForRetry(delayMs) + if (abortController.signal.aborted || isStale()) { + return { attached: false, hadStreamError: false, aborted: true } + } + } + if (!streamId && chatId) { streamId = (await getActiveStreamIdForChat(chatId)) ?? undefined } @@ -1340,12 +1360,12 @@ export function useChat( } } } - - if (attempt < RECOVERY_RETRY_DELAYS_MS.length) { - await waitForRetry(RECOVERY_RETRY_DELAYS_MS[attempt] ?? 1000) - } } + logger.error('All reconnect attempts exhausted', { + streamId, + maxAttempts: MAX_RECONNECT_ATTEMPTS, + }) setError(lastError) return { attached: false, hadStreamError: true, aborted: false } } finally {