首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >WorkBuddy 添加空间总是失败?一次从日志到代码的完整排查实录

WorkBuddy 添加空间总是失败?一次从日志到代码的完整排查实录

原创
作者头像
用户10770059
发布于 2026-09-23 12:34:08
发布于 2026-09-23 12:34:08
1811
举报

环境:Windows 10 22H2 (19045) | WorkBuddy 5.6.2 | 默认工作空间根目录 C:\Users\你的用户名\WorkBuddy\ 结论先行:"添加空间失败"不是功能坏了,而是名称与根目录下已有文件夹重名,且客户端把错误原因吞掉了。

一、问题现象

在 WorkBuddy 左侧边栏的「空间」分组点击 +(新建空间),输入名称确认后,添加必定失败。界面只弹一个含糊的失败提示,不告诉你为什么失败;换个名字再点,还是失败。

大多数人到这一步就放弃了。但这类"失败但不说原因"的问题,日志里通常写得明明白白。下面把完整排查过程记录下来,方法可以复用到任何 WorkBuddy 客户端异常上。


二、第一步:翻日志,先看看客户端到底报了什么

WorkBuddy 的日志在用户目录下,按日期分目录存放:

代码语言:javascript
复制
%USERPROFILE%\.workbuddy\logs\
├── main.log                  # 主进程日志(关键接口调用、错误都在这里)
├── renderer.log              # 渲染进程日志(界面层)
├── main.old.log / renderer.old.log   # 滚动归档
└── 2026-09-23\               # 按天的会话日志

用命令行按关键词过滤,比肉眼翻文件快得多。在 Git Bash 或 WSL 里这样查:

代码语言:javascript
复制
# 找出所有和 workspace 相关、且带 fail/error/exist 的日志行
grep -i "workspace" ~/.workbuddy/logs/main.log \
  | grep -i -E "fail|error|denied|invalid|exist"

命中两条(时间正好对上点击按钮的时刻):

代码语言:javascript
复制
{"timestamp":"2026-09-23T03:17:19.421Z","level":"error","scope":"main",
 "message":["[WB-SDK] invoke error on \"wb:workspaces:generate\": Workspace already exists"]}
{"timestamp":"2026-09-23T03:17:23.256Z","level":"error","scope":"main",
 "message":["[WB-SDK] invoke error on \"wb:workspaces:generate\": Workspace already exists"]}

关键信息拿到了:失败发生在 wb:workspaces:generate 这个接口,错误是 Workspace already exists。

也就是说 —— 失败原因是"空间已存在"。可问题来了:输入的名字明明是新的,怎么会"已存在"?


三、第二步:看看根目录里到底有什么

WorkBuddy 的默认工作空间根目录就是一个普通文件夹,直接列出来:

代码语言:javascript
复制
ls -la "$USERPROFILE/WorkBuddy/"
代码语言:javascript
复制
drwxr-xr-x  2026-09-21-16-25-54
drwxr-xr-x  2026-09-21-18-13-06
drwxr-xr-x  2026-09-22-10-23-31
drwxr-xr-x  2026-09-22-13-20-30
drwxr-xr-x  Claw
drwxr-xr-x  国网勘察管控系统
drwxr-xr-x  岩土勘察AI平台

原因浮出水面:每个"空间"在磁盘上就是一个同名文件夹。所谓"新建空间",本质是在这个根目录下执行 mkdir。

Windows 的文件系统不区分大小写 —— 所以哪怕输入的只是大小写不同(比如 claw 与已存在的 Claw),系统一样判定为"已存在"。


四、第三步:从客户端资源包确认逻辑分支

日志只告诉你"失败",要确认"为什么没有兜底处理",就得看客户端自己的实现。WorkBuddy 是基于 Electron 的,业务代码打包在资源包里:

代码语言:javascript
复制
<安装目录>\WorkBuddy\resources\app.asar

app.asar 是一个拼接式归档(前面是 JSON 目录头,后面是文件内容),不解包也能直接按字符串检索。用 Python 扫描一遍,把错误文案前后的代码打出来:

代码语言:javascript
复制
data = open(r'D:\Program Files\wb\WorkBuddy\resources\app.asar', 'rb').read()
needle = b'Workspace already exists'
i = 0
while True:
    i = data.find(needle, i)
    if i < 0:
        break
    print(data[i-600:i+300].decode('utf-8', 'replace'))
    i += 1

拿到 WorkspaceDomain.generate 的真实实现:

代码语言:javascript
复制
async generate(params) {
    const root = await this.providers.resolveDefaultRoot();
    await ensureRootDirReady(root);
    if (params?.name !== void 0) {
        const candidate = path.join(root, normalizeWorkspaceName(params.name));
        try {
            await fs.mkdir(candidate);            // ← 直接建目录
        } catch (error) {
            if (error.code === "EEXIST") {        // ← 目录已存在
                throw new WBError("Workspace already exists", {
                    code: "CONFLICT", source: "wb.workspaces.generate", cause: error
                });                               // ← 直接抛错,无兜底
            }
            throw error;
        }
        // ...
    }
}

再看界面层怎么处理这个错误:

代码语言:javascript
复制
const handleConfirmCreateWorkspace = useCallback(async () => {
    const name = createWorkspaceName.trim();
    if (!name || creatingWorkspaceRef.current) return;
    try {
        const { path } = await wb.workspaces.generate({ name });
        // ...
    } catch (error) {
        console.warn("[ConversationList] create workspace threw:", error);
        toast.error(t("delete.toast.failed"));    // ← 复用了「删除失败」的文案
    }
}, [createWorkspaceName]);

两处问题一次看清:

  1. 服务端不做兜底:手输名称这条路径,mkdir 撞上 EEXIST 就直接抛 CONFLICT,不重试、不建议新名字。(对比:不传名称走"自动生成时间戳空间"的路径,配有 GENERATE_MAX_ATTEMPTS = 100 的秒级冲突重试。两条路标准不一致。)
  2. 前端提示误导:错误提示文案借用了 delete.toast.failed(删除失败),跟实际原因毫无关系,所以用户只能看到"失败了",永远猜不到是重名。

五、根因与规避

根因:新建空间的名称与 %USERPROFILE%\WorkBuddy\ 下已有文件夹重名(含仅大小写不同),服务端抛 CONFLICT,前端提示错误。

立刻可用的规避方式:

场景

做法

想新建一个全新空间

换一个根目录下没用过的名称

想指向一个已存在的文件夹

别走「+ 新建」,改用输入框左下角「选择工作空间 / 打开本地文件夹」

分不清哪些名字被占了

直接看一眼 %USERPROFILE%\WorkBuddy\ 目录(界面上被移除的空间,文件夹可能还留在磁盘上)


六、给官方的四条改进建议

  1. 弹窗内实时校验重名:输入时就检查目录是否存在,命中则按钮置灰并提示「该名称已被占用」。目前「重命名空间」已有实时校验,「新建空间」却没有,两者应统一。
  2. 错误语义化:为 CONFLICT 单独补一条 i18n 文案(如「空间名已存在,请换一个名称」),不要再复用删除失败的 key。
  3. 手输路径也支持去重:提供「使用推荐名称(追加序号/时间戳)」的一键操作,或让 GENERATE_MAX_ATTEMPTS 的重试策略同样覆盖显式命名。
  4. 错误带 traceId:失败 toast 里给一个「反馈」入口,自动附上 traceId,把用户反馈成本降到最低。

七、这套排查方法可以复用到哪些场景

这套「日志 → 目录 → 资源包」三步法,几乎通用于所有 WorkBuddy 客户端异常:

  • 先看日志:%USERPROFILE%\.workbuddy\logs\main.log 是主战场,按关键词加时间戳过滤,绝大多数接口层错误都有原文。
  • 再验证假设:日志说的是"已存在",就去文件系统里验证,别急着下结论。
  • 最后看实现:业务逻辑打包在 resources\app.asar,字符串检索即可定位函数,配合日志里的错误码能准确判断是"设计如此"还是"真缺陷"。
  • 倒过来也是排查思路:界面上莫名其妙的一句提示(比如本文的"删除失败"却出现在创建场景),往往就是文案复用导致的误导,顺着 i18n key 能找到真正的逻辑分支。

八、小结

项

内容

现象

新建空间必定失败,提示不明确

真因

名称与根目录已有文件夹重名(Windows 大小写不敏感)

表象误导

报错文案复用了「删除失败」

规避

换名字,或改用「选择工作空间」指向已有目录

建议

实时校验+独立文案+自动去重+traceId

一句话经验:输入类的操作,凡是"失败但不说为什么"的,先去看日志里的接口名和错误码 —— 十有八九是重名、越权、路径不存在这三种之一。


本文基于客户端日志与资源包静态分析得出,未修改任何客户端文件。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、问题现象
  • 二、第一步:翻日志,先看看客户端到底报了什么
  • 三、第二步:看看根目录里到底有什么
  • 四、第三步:从客户端资源包确认逻辑分支
  • 五、根因与规避
  • 六、给官方的四条改进建议
  • 七、这套排查方法可以复用到哪些场景
  • 八、小结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档