From c1cad440b134273d9d6a692efc5607c048466574 Mon Sep 17 00:00:00 2001 From: kalpadhwaryu Date: Wed, 15 Oct 2025 16:42:11 +0530 Subject: [PATCH 1/3] fix: Event Source creation error --- frontend/src/hooks/useChatStream.ts | 96 +++++++++++++++----- frontend/src/routes/_authenticated/agent.tsx | 8 +- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/frontend/src/hooks/useChatStream.ts b/frontend/src/hooks/useChatStream.ts index 760944628..9643d36b0 100644 --- a/frontend/src/hooks/useChatStream.ts +++ b/frontend/src/hooks/useChatStream.ts @@ -219,28 +219,82 @@ const appendReasoningData = (streamState: StreamState, data: string) => { export async function createAuthEventSource(url: string): Promise { return new Promise((resolve, reject) => { let triedRefresh = false + let isResolved = false + let currentEventSource: EventSource | null = null + + const cleanup = () => { + if (currentEventSource) { + currentEventSource.onopen = null + currentEventSource.onerror = null + if (currentEventSource.readyState !== EventSource.CLOSED) { + currentEventSource.close() + } + } + } - const make = () => { - const es = new EventSource(url, { withCredentials: true }) + const tryRefreshAndRetry = async () => { + if (triedRefresh) { + reject(new Error("Connection failed after token refresh attempt")) + return + } + + triedRefresh = true + try { + const refresh = await fetch("/api/v1/refresh-token", { + method: "POST", + credentials: "include", + }) + + if (refresh.ok) { + // Small delay before retry to avoid rapid reconnection + setTimeout(() => make(), 100) + } else { + reject(new Error("Token refresh failed")) + } + } catch (e) { + reject(new Error("Token refresh failed")) + } + } - es.onopen = () => resolve(es) + const make = () => { + try { + cleanup() // Clean up any previous attempt + const es = new EventSource(url, { withCredentials: true }) + currentEventSource = es + + // Set a timeout for the connection attempt + const connectionTimeout = setTimeout(() => { + if (!isResolved) { + cleanup() + tryRefreshAndRetry() + } + }, 5000) // 5 second timeout - es.onerror = async () => { - // es.close() + es.onopen = () => { + if (!isResolved) { + clearTimeout(connectionTimeout) + isResolved = true + resolve(es) + } + } - if (!triedRefresh) { - triedRefresh = true - // Attempt token refresh - const refresh = await fetch("/api/v1/refresh-token", { - method: "POST", - credentials: "include", - }) - if (!refresh.ok) return reject(new Error("Token refresh failed")) + es.onerror = async (e) => { + clearTimeout(connectionTimeout) + + if (isResolved) { + // If already resolved, don't handle the error here + return + } - // Retry opening the stream - make() - } else { - reject(new Error("SSE connection failed after refresh")) + // Check if EventSource is in a failed state + if (es.readyState === EventSource.CLOSED) { + cleanup() + await tryRefreshAndRetry() + } + } + } catch (error) { + if (!isResolved) { + reject(new Error(`Failed to create EventSource: ${error instanceof Error ? error.message : 'Unknown error'}`)) } } } @@ -352,8 +406,8 @@ export const startStream = async ( } catch (err) { console.error("Failed to create EventSource:", err) toast({ - title: "Failed to create EventSource", - description: "Failed to create EventSource", + title: "Error", + description: "Something went wrong. Please try again.", variant: "destructive", }) return @@ -977,8 +1031,8 @@ export const useChatStream = ( } catch (err) { console.error("Failed to create EventSource:", err) toast({ - title: "Failed to create EventSource", - description: "Failed to create EventSource", + title: "Error", + description: "Something went wrong. Please try again.", variant: "destructive", }) return diff --git a/frontend/src/routes/_authenticated/agent.tsx b/frontend/src/routes/_authenticated/agent.tsx index 8d84c19d6..de1d3161a 100644 --- a/frontend/src/routes/_authenticated/agent.tsx +++ b/frontend/src/routes/_authenticated/agent.tsx @@ -1028,8 +1028,8 @@ function AgentComponent() { } catch (err) { console.error("Failed to create EventSource:", err) toast({ - title: "Failed to create EventSource", - description: "Failed to create EventSource", + title: "Error", + description: "Something went wrong. Please try again.", variant: "destructive", }) return @@ -2396,8 +2396,8 @@ function AgentComponent() { } catch (err) { console.error("Failed to create EventSource:", err) toast({ - title: "Failed to create EventSource", - description: "Failed to create EventSource", + title: "Error", + description: "Something went wrong. Please try again.", variant: "destructive", }) return From 1546436a64b64b4c57337b70247ff48f6aba64bd Mon Sep 17 00:00:00 2001 From: kalpadhwaryu Date: Wed, 15 Oct 2025 18:26:01 +0530 Subject: [PATCH 2/3] fix: Add retry mechanism for event source creation error --- frontend/src/hooks/useChatStream.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/hooks/useChatStream.ts b/frontend/src/hooks/useChatStream.ts index 9643d36b0..52c01209b 100644 --- a/frontend/src/hooks/useChatStream.ts +++ b/frontend/src/hooks/useChatStream.ts @@ -219,6 +219,8 @@ const appendReasoningData = (streamState: StreamState, data: string) => { export async function createAuthEventSource(url: string): Promise { return new Promise((resolve, reject) => { let triedRefresh = false + let retryCount = 0 + const maxRetries = 3 let isResolved = false let currentEventSource: EventSource | null = null @@ -234,7 +236,16 @@ export async function createAuthEventSource(url: string): Promise { const tryRefreshAndRetry = async () => { if (triedRefresh) { - reject(new Error("Connection failed after token refresh attempt")) + // After refresh, try up to 3 more times before giving up + if (retryCount >= maxRetries) { + reject(new Error(`Connection failed after token refresh and ${maxRetries} retry attempts`)) + return + } + + retryCount++ + // Exponential backoff: 100ms, 200ms, 400ms + const delay = 100 * Math.pow(2, retryCount - 1) + setTimeout(() => make(), delay) return } From c4b679963dd3e51301437d7244182efcf00a111c Mon Sep 17 00:00:00 2001 From: kalpadhwaryu Date: Wed, 15 Oct 2025 19:41:33 +0530 Subject: [PATCH 3/3] fix: XYNE-201 Correct description for error toast n --- frontend/src/routes/_authenticated/integrations/apiKey.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/src/routes/_authenticated/integrations/apiKey.tsx b/frontend/src/routes/_authenticated/integrations/apiKey.tsx index de3f0d49a..e38772119 100644 --- a/frontend/src/routes/_authenticated/integrations/apiKey.tsx +++ b/frontend/src/routes/_authenticated/integrations/apiKey.tsx @@ -255,8 +255,7 @@ const ApiKeyComponent = ({ user, agentWhiteList }: ApiKeyProps) => { } catch (error) { toast({ title: "Error", - description: - error instanceof Error ? error.message : "Failed to create API key", + description: "Failed to create API key", variant: "destructive", }) } finally { @@ -294,8 +293,7 @@ const ApiKeyComponent = ({ user, agentWhiteList }: ApiKeyProps) => { } catch (error) { toast({ title: "Error", - description: - error instanceof Error ? error.message : "Failed to revoke API key", + description: "Failed to revoke API key", variant: "destructive", }) }