macOS 上的 VS Code 为什么会神秘消失?
macOS 上的 VS Code 为什么会神秘消失?
一次很离谱的经历:Dock 里的 VS Code 图标突然变成通用应用图标,Finder 里原本的 Visual Studio Code.app 也不见了。最后排查下来,问题出在 VS Code 自动更新、ShipIt 后台进程和跨 APFS 卷移动之间。
起因
凌晨打开 Mac mini 准备工作,习惯性点击 Dock 上的 VS Code 图标,结果图标变成了空白的 macOS 通用应用图标。

打开 Finder 到应用所在目录,也能看到 VS Code 状态异常:

我的 VS Code 没装在系统默认的 /Applications/,而是在外置 APFS 卷的应用目录里。
第一反应:app 去哪了?
先检查应用目录:
ls -la "/Volumes/<external-volume>/applications/" | grep -i "code\|visual"没有任何输出。也就是说,Visual Studio Code.app 目录已经不存在。
但 Dock 里还保留着旧路径:
defaults read com.apple.dock persistent-apps | grep -A2 "Visual Studio Code"输出里还能看到类似路径:
file:///Volumes/<external-volume>/applications/Visual%20Studio%20Code.app/Dock 记住了一个已经不存在的 app,所以 macOS 只能显示通用图标。
真凶:VS Code 自动更新
继续查系统临时目录,发现了 VS Code 的更新器痕迹:
find /private/var/folders -name "*.ShipIt*" -maxdepth 4能看到多个类似目录:
/private/var/folders/.../T/com.microsoft.VSCode.ShipIt.xxxxxxxx进入其中一个目录,可以看到完整的 Visual Studio Code.app 包。也就是说,旧版本已经从目标目录删除,新版本却停在了临时目录里,没有成功回到安装位置。
查看临时目录里 app 的版本:
defaults read "/private/var/folders/.../ShipIt.xxxxxxxx/Visual Studio Code.app/Contents/Info.plist" CFBundleShortVersionString确认它就是更新后的新版本。
结论很明确:VS Code 自动更新器删除了旧版本,但新版本移动回原位置时失败了。
为什么会失败?
VS Code 使用 Electron 的 Squirrel.Mac 做自动更新,核心辅助进程叫 ShipIt。大致流程是:
1. 检查更新并下载新版本
2. 解压到 /private/var/folders/.../T/
3. 启动 ShipIt 辅助进程
4. ShipIt 等待 VS Code 退出
5. 删除旧的 Visual Studio Code.app
6. 把新 app 移动到原安装位置问题出在临时目录和安装目录不在同一个卷。
macOS 的临时目录通常在启动卷:
/private/var/folders/<hash>/T/而我的 VS Code 在外置 APFS 卷:
/Volumes/<external-volume>/applications/Visual Studio Code.app同卷移动通常只是改目录项,速度很快;跨卷移动则会变成复制再删除。这个过程不是原子的,只要复制中断、目标卷权限异常、磁盘空间不足、卷短暂不可用,就可能出现“旧的删了,新的没到”的状态。
这不是 VS Code 独有的问题。只要更新器把新版本放在启动卷临时目录,再把 app 移到另一个卷,就有类似风险。
为什么重装后又消失?
第一次我从临时目录把 app 移回去:
mv "/private/var/folders/.../T/com.microsoft.VSCode.ShipIt.xxxxxxxx/Visual Studio Code.app" \
"/Volumes/<external-volume>/applications/"然后在 VS Code 设置里禁用自动更新:
{
"update.mode": "none"
}本以为问题结束了,结果用 DMG 重装后,VS Code 又被删了一次。
原因是 ShipIt 是独立后台进程。旧版 VS Code 之前已经触发过更新,ShipIt 可能还在后台排队等待执行。update.mode: "none" 只在 VS Code 主进程读取配置时生效,挡不住已经启动的 ShipIt。
最终修复
1. 杀掉 ShipIt 进程
pkill -f "ShipIt"2. 从最新临时目录恢复 app
mv "/private/var/folders/.../T/com.microsoft.VSCode.ShipIt.xxxxxxxx/Visual Studio Code.app" \
"/Volumes/<external-volume>/applications/"3. 禁用自动更新器执行权限
关键是让 ShipIt 二进制无法继续运行:
chmod -x "/Volumes/<external-volume>/applications/Visual Studio Code.app/Contents/Frameworks/Squirrel.framework/Versions/A/Resources/ShipIt"确认执行权限已经去掉:
ls -la "/Volumes/<external-volume>/applications/Visual Studio Code.app/Contents/Frameworks/Squirrel.framework/Versions/A/Resources/ShipIt"如果权限里没有 x,说明它已经不能直接执行。
4. 清理临时更新目录
rm -rf /private/var/folders/.../T/com.microsoft.VSCode.ShipIt.*这一步要小心路径,确认只删除 VS Code 的 ShipIt 临时目录。
修复效果
- VS Code 可以正常启动。
- Dock 图标恢复正常。
- 退出后可以重新打开。
- 自动更新不会再启动 ShipIt。
- 以后需要手动下载 DMG 或用包管理器更新。
经验教训
update.mode: "none" 不够
它只能阻止 VS Code 主进程后续触发更新。对已经运行的 ShipIt 后台进程无效。
App 装在非系统卷会增加更新风险
如果必须把 app 放在外置卷,建议禁用自动更新,改成手动更新。否则每次自动更新都可能触发跨卷复制。
临时目录经常能救命
更新失败时,新版本 app 往往还完整地躺在:
/private/var/folders/.../T/com.microsoft.VSCode.ShipIt.xxxxxxxx/不要急着重新下载,先找临时目录。
ShipIt 是独立进程
关掉 VS Code 不代表更新器已经停止。排查这类问题时,要单独检查和清理 ShipIt。
技术备忘
| 项目 | 内容 |
|---|---|
| 更新框架 | Squirrel.Mac |
| 辅助进程 | ShipIt |
| 临时目录 | /private/var/folders/<hash>/T/com.microsoft.VSCode.ShipIt.<随机>/ |
| 风险点 | 临时目录和 app 安装目录跨卷 |
| 表现 | 旧 app 被删除,新 app 未移回 |
| 最终处理 | pkill ShipIt、恢复 app、chmod -x ShipIt |
时间线
| 时间 | 事件 |
|---|---|
| 4 月 29 日 | ShipIt 开始更新尝试 |
| 4 月 30 日 | 新版本解压完成,旧版本被删除,跨卷移动失败 |
| 5 月 1 日 | 发现 VS Code 图标异常并开始排查 |
| 5 月 1 日 | 定位到 ShipIt 临时目录 |
| 5 月 1 日 | DMG 重装后再次被 ShipIt 删除 |
| 5 月 1 日 | 通过禁用 ShipIt 执行权限彻底解决 |
总结
这次问题的根因不是 VS Code 本体损坏,而是自动更新器在跨卷场景下没有做到真正安全的替换流程。
如果应用装在系统卷之外,尤其是外置 APFS 卷,建议对带自动更新的 Electron 应用保持警惕。最稳的方案是禁用应用内自动更新,改为手动下载或使用包管理器统一更新。
