Group chat opens then silently bounces back to previous page (ChatHydration × GroupIdSync race, Canary)

在 Canary 版本桌面端点击侧边栏群组会话时,页面会短暂进入群组路由然后又静默跳回之前的智能体/话题页。优先怀疑 ChatHydration 的主题订阅与 GroupIdSync 卸载清理之间存在竞态条件。

快速结论:在 Canary 版本桌面端点击侧边栏群组会话时,页面会短暂进入群组路由然后又静默跳回之前的智能体/话题页。优先怀疑 ChatHydration 的主题订阅与 GroupIdSync 卸载清理之间存在竞态条件。

适用环境:LobeChat Desktop Canary 渠道,版本 2.2.14-canary.80 / 2.2.14-canary.81(两个版本均可复现);macOS arm64 + Electron。Issue 中并未确认 Web 端是否可复现。

最快修复方案:暂无确认的一步修复方案。该问题在 Issue 关闭时仍处于根因分析阶段,官方尚未合并针对此竞态的修复 PR。

注意事项:下方解决步骤为基于 Issue 讨论的推断方案,属于“可优先尝试”范畴,未经官方验证,请先在 Canary 测试环境中验证效果。

问题场景

在 LobeChat Canary 桌面版中,点击侧边栏任意群组会话(路由形如 app://renderer/group/cg_...)时,地址栏会短暂切换到群组页面,随后 UI 静默跳回之前的智能体/话题页,群组会话完全无法使用。通过 Chromium DevTools Protocol 探测确认:路由切换本身成功(readyState: complete),没有崩溃、没有错误边界渲染、也没有任何控制台报错,跳转过程完全静默。

报错原文

Group chat opens then silently bounces back to previous page (ChatHydration × GroupIdSync race, Canary)

11:30:04 → app://renderer/group/cg_OeHhR8d7wrwX   (clicked group A — route OK, readyState complete)
11:30:24 → app://renderer/group/cg_gd2LdhiuIH6m   (clicked group B — route OK, readyState complete)
11:31:00 → app://renderer/agent/agt_.../tpc_...    (bounced BACK to the original agent topic page)

Persistent: ResizeObserver loop completed with undelivered notifications
On topic open: TRPCClientError: No running operation found on this topic

原因分析

Issue 中的主要假设是一个竞态条件:ChatHydrationGroupIdSync 的卸载清理互相竞争,可能原因如下——

点击群组 B 后路由切换成功,ChatHydration 挂载并订阅 useChatStore.activeTopicIdGroupIdSync 在切换群组时调用 switchTopic(null) 并将 activeTopicId 置为 null。问题出在 GroupIdSyncunmount 清理上:它会在卸载时执行 useAgentGroupStore.setState({ activeGroupId: undefined })。如果旧群组的 unmount 发生在 ChatHydration 订阅新群组 activeTopicId 之后,useGroupContext 会读到 currentGroup = undefined,导致 groupId = nullagentId = '',整个会话上下文在首次渲染时为空。ChatHydration 内部基于过期的上下文调用 navigate(nextUrl, { replace: true }),从而把用户弹回原页面。

另外,Issue 明确指出:先前修复了 agent 会话中类似竞态的 PR #14231,但群组侧一直没有做同等保护;现有 PR #16149 只修复了数据持久化路径,未保护 ChatHydration 的导航路径。

环境排查

  • Desktop 版本:2.2.14-canary.802.2.14-canary.81;更早的 2.2.14-canary.74 也有崩溃表现(见 #18210)。
  • 操作系统:macOS arm64,Electron 环境。
  • 确认是否为 Desktop 特有:竞态位于渲染进程(React/Zustand/react-router),Web 客户端理论上也可能触发,但 Electron 单持久 webview 的组件卸载时序更容易复现;若条件允许,可在 Web 端快速连续点击群组尝试复现。
  • 可借由 CDP 探测确认路由状态:--remote-debugging-port=9222 + --enable-logging

解决步骤

  1. 确认问题可稳定复现后,先检查 zustand 状态发散:在跳转窗口期间分别检查 chatStore.activeGroupIdagentGroupStore.activeGroupId。如果两个值不一致(一个为新群组 ID、另一个为 undefined),则基本坐实 unmount 竞态。
  2. 可优先尝试的修复方向一:修改 GroupIdSync.tsxuseUnmount 清理逻辑,增加守卫条件——只有在“新群组的 GroupIdSync 尚未挂载”时才清除 store 状态,避免旧群组卸载时误清新群组的上下文。
  3. 可优先尝试的修复方向二:在 ChatHydration 调用 navigate 之前增加防御性校验——确认 gid 非空且与 agentGroupStore.activeGroupId 一致,不匹配则放弃导航,避免用过期上下文触发错误跳转。
  4. 参考此前 agent 会话的修复先例 PR #14231(清除 agent 切换时的陈旧 topic),在群组场景做同等处理——旧群组卸载时清除陈旧的 activeTopicId。
  5. 修改后请在 Canary 构建中回归验证:快速点击多个不同群组,确认不再弹回原页面。

验证方法

修复后重复 Issue 中的复现步骤:在侧边栏连续点击多个群组,观察浏览器地址是否稳定停留在 app://renderer/group/cg_...,不再弹回之前的 agent/.../topic/... 页面。同时用 CDP 探测确认 location.href 在点击后不再发生回跳;也可以检查两个 zustand store 的 activeGroupId 是否保持一致。

参考来源

lobehub/lobe-chat #18243

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 20153

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注