From 7af61e121e4e87a5d3060b8ee2fe1d4357a1befb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Guti=C3=A9rrez?= <35082514+alezmad@users.noreply.github.com> Date: Sat, 2 May 2026 19:19:25 +0100 Subject: [PATCH] fix(web): stop SSE reconnect loop on 4xx errors A revoked api key or missing topic returned by GET /v1/.../stream used to throw inside the catch and bounce through the backoff loop forever. Now any 4xx response terminates the loop and surfaces the status + body in the panel error so the user sees the real cause. 5xx and network errors still reconnect. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/web/src/modules/mesh/topic-chat-panel.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/web/src/modules/mesh/topic-chat-panel.tsx b/apps/web/src/modules/mesh/topic-chat-panel.tsx index c2270a1..04cb4d8 100644 --- a/apps/web/src/modules/mesh/topic-chat-panel.tsx +++ b/apps/web/src/modules/mesh/topic-chat-panel.tsx @@ -243,6 +243,15 @@ export function TopicChatPanel({ cache: "no-store", }, ); + // 4xx is terminal — auth invalid, key revoked, topic gone. + // Reconnecting won't fix any of those, so surface the error + // and stop. 5xx and network errors fall through to backoff. + if (res.status >= 400 && res.status < 500) { + const body = await res.text().catch(() => ""); + setError(`stream halted: ${res.status} ${body.slice(0, 200)}`); + setStreamState("stopped"); + return; + } if (!res.ok || !res.body) { throw new Error(`stream open failed: ${res.status}`); }