From 0122b8bb07fbaa373db9d1462b09666a3ad061b2 Mon Sep 17 00:00:00 2001 From: "tao.chen" <93983997+taochen-ct@users.noreply.github.com> Date: Mon, 6 Jul 2026 17:37:05 +0800 Subject: [PATCH] fix(web): break infinite loop in TerminalTabs when shell list is empty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TerminalTabs's hydrate effect set activeShellId to "" whenever the list was empty or the current active was missing. "" is falsy, so on the next render the same branch fired again with currentActive still "" and ids still [] — setActiveShell(ws, "") in a tight loop, every render triggering a Zustand store update which re-rendered the component. Maximum update depth exceeded. Fix: only set the active shell when ids has at least one entry. The auto-start effect above already creates a shell when the list is empty, so the next list refetch will populate ids and this branch will pick a real candidate naturally. --- web/src/components/layout/WorkspaceShell.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/web/src/components/layout/WorkspaceShell.tsx b/web/src/components/layout/WorkspaceShell.tsx index 32e140e..9ee3bff 100644 --- a/web/src/components/layout/WorkspaceShell.tsx +++ b/web/src/components/layout/WorkspaceShell.tsx @@ -107,8 +107,13 @@ function TerminalTabs({ workspaceId }: { workspaceId: string }) { setShellsForWorkspace(workspaceId, ids); } const currentActive = activeShellId; - if (!currentActive || !ids.includes(currentActive)) { - setActiveShell(workspaceId, ids[0] ?? ""); + // Only set an active shell when the list is non-empty. If ids is empty, + // the auto-start effect above will create a shell; the next list refetch + // will populate ids and this branch will run with a real candidate. + // (Setting it to "" here would loop: "" is falsy, so the same branch + // would fire on the next render.) + if (ids.length > 0 && (!currentActive || !ids.includes(currentActive))) { + setActiveShell(workspaceId, ids[0]); } }, [ workspaceId,