# 排错总结:`/workspace/chats/new` 场景下 Artifact 图片访问异常 ## 背景 在以下 URL 场景中: `/workspace/chats/new?isnew=false&thread_id=b9a30765-0575-44c9-954e-bdaaf3d083fa&xclaw_used=true` 页面会复用已有线程,并跳转到: `/workspace/chats/b9a30765-0575-44c9-954e-bdaaf3d083fa` 但出现现象: - `.../api/threads/b9.../artifacts/mnt/user-data/outputs/cat-generated.jpg` 返回 `Artifact not found` - `.../api/threads/new/artifacts/mnt/user-data/outputs/cat-generated.jpg` 可以访问 ## 现象与证据 本地线程目录检查结果显示: - `backend/.deer-flow/threads/new/user-data/outputs/cat-generated.jpg` 存在 - `backend/.deer-flow/threads/b9a30765-0575-44c9-954e-bdaaf3d083fa/user-data/outputs/` 不存在对应文件 这说明文件实际写入了 `thread_id = "new"` 的目录,而不是目标线程 `b9...`。 ## 根因 问题由“线程 ID 来源不一致”触发: 1. 页面层已经解析出“真实线程 ID”(query 中的 `thread_id`)。 2. 消息渲染组件中仍直接读取路由参数 `useParams().thread_id`。 3. 在 `/workspace/chats/new` 下,路由参数固定是 `"new"`,导致图片/文件 URL 被拼接为 `/api/threads/new/artifacts/...`。 4. 输入与提交链路存在对 `params.thread_id` 的兜底,存在将 `"new"` 带入写操作的风险。 ## 修复内容 本次修复统一了线程 ID 的来源,并增加了防御性校验: 1. 消息渲染链路统一使用页面透传的解析后 `threadId` - `MessageListItem` 移除 `useParams().thread_id` - `MessageList` 将 `threadId` 透传给 `MessageListItem` 2. 输入组件移除路由参数兜底 - `InputBox` 不再使用 `threadIdFromProps ?? params?.thread_id` - 仅使用上游传入的解析后 `threadId` 3. 提交流程增加硬保护 - 在 `useThreadStream` / `useSubmitThread` 中拦截 `threadId === "new"`,阻止上传和提交继续执行 4. 复用旧线程时强制路径对齐 - 在 `/workspace/chats/new` 且命中复用条件时,立即 `replace` 到 `/workspace/chats/{thread_id}` ## 涉及文件 - `frontend/src/components/workspace/messages/message-list-item.tsx` - `frontend/src/components/workspace/messages/message-list.tsx` - `frontend/src/components/workspace/input-box.tsx` - `frontend/src/core/threads/hooks.ts` - `frontend/src/components/workspace/chats/use-thread-chat.ts` ## 验证结果 - TypeScript 编译检查通过:`pnpm -C frontend exec tsc --noEmit --pretty false` - 逻辑上可确保后续不会再写入 `threads/new/...`。 ## 影响与后续处理 本次修复不自动迁移历史文件。 已落在 `threads/new/...` 的旧产物,仍需要: 1. 手动迁移到目标线程目录,或 2. 在目标线程中重新生成。