浓眉大眼的 Bun,怎么也有这种巨坑?

0. 前言

今晚被 Bun 狠狠地教育了几个小时。

事情是这样的:我在开发一个微信短链接工具 wx-shortlink-tool,为了实现多平台分发,写了一个基于 Bun 的三平台交叉构建脚本(Cross-Platform Build)。原本在公司电脑上跑得顺风顺水,结果回到家里的开发机上,直接陷入了无限构建失败的死循环

如果你在使用 Windows 系统,且项目没有放在 C 盘,那么你在用 Bun 进行 bun build --compile 跨平台打包时,大概率也会遇到这个让人抓狂的 Bug。


1. 诡异的现象与“假预警”

在项目根目录(D:\workspace\wx-shortlink-tool)下执行打包脚本,编译到 Mac 平台(bun-darwin-aarch64)时,终端死活卡在下载进度条。

更离谱的是,每次眼睁睁看着进度条下载到 100%(文件大小和 GitHub Release 上的完全一致),紧接着就会甩出这样一句极其无情的报错:

Failed to extract executable for 'bun-darwin-aarch64-v1.3.14'. The download may be incomplete.

# 踩坑记录:Bun 跨平台编译的“史诗级”巨坑(Failed to extract executable 跨盘符移动失败 Bug)

## 0. 前言
今晚被 Bun 狠狠地教育了几个小时。

事情是这样的:我在开发一个微信短链接工具 `wx-shortlink-tool`,为了实现多平台分发,写了一个基于 Bun 的三平台交叉构建脚本(Cross-Platform Build)。原本在公司电脑上跑得顺风顺水,结果回到家里的开发机上,直接陷入了无限死循环的噩梦。

如果你在使用 Windows 系统,且**项目没有放在 C 盘**,那么你在用 Bun 进行 `bun build --compile` 跨平台打包时,大概率也会遇到这个让人抓狂的假死 Bug。

---

## 1. 诡异的现象与“假预警”

在项目根目录(`D:\workspace\wx-shortlink-tool`)下执行打包脚本,编译到 Mac 平台(`bun-darwin-aarch64`)时,终端死活卡在下载进度条。

更离谱的是,每次眼睁睁看着进度条下载到 100%(文件大小和 GitHub Release 上的完全一致),紧接着就会甩出这样一句极其无情的报错:

```bash
Failed to extract executable for 'bun-darwin-aarch64-v1.3.14'. The download may be incomplete.

1. 诡异的现象与“假预警”

在项目根目录(D:\workspace\wx-shortlink-tool)下执行打包脚本,编译到 Mac 平台(bun-darwin-aarch64)时,终端死活卡在下载进度条。

更离谱的是,每次眼睁睁看着进度条下载到 100%(文件大小和 GitHub Release 上的完全一致),紧接着就会甩出这样一句仿佛是你网络有问题导致下载失败的报错:

Failed to extract executable for 'bun-darwin-aarch64-v1.3.14'. The download may be incomplete.

正常的排查思路(全踩空了):

  1. 网络问题? 挂代理、换源、甚至手动去 GitHub 浏览器把 bun-darwin-aarch64.zip 下下来。没用,它依然会因为解压失败而弹出上面的提示。
  2. 环境污染? 把全局 Bun 删得干干净净,锁死项目本地 node_modules/.bin/bun。没用。

2. 破案:GitHub Issue #25346

在翻遍了犄角旮旯后,终于在 Bun 的 GitHub Issue 列表里找到了同病相怜的冤大头:Issue #25346

看完评论,我直接一口老血喷出来。这根本不是网络问题,也不是解压问题,而是一个极其隐蔽的 Windows 跨盘符移动 Bug

罪魁祸首的演化路径:

  1. 跨盘下载:当你在 D 盘D:\workspace\...)执行打包命令时,Bun 会在当前工作目录(D 盘)创建一个临时文件夹来接收下载的 Mac 基础环境包(约 90MB+)。
  2. 原子移动(崩溃点):解压完成后,Bun 会调用系统底层的 fs.rename API,试图把这个弄好的二进制文件从 D 盘的临时目录 移动到你 C 盘的全局缓存目录(C:\Users\ma--z\.bun\install\...)。
  3. 操作系统的铁律:在 Windows 和 Unix 底层,系统是不支持跨磁盘分区(Cross-Drive)执行原子的 rename 操作的(底层会抛出 EXDEV: cross-device link not permitted)。
  4. 粗暴的毁尸灭迹:Bun 的错误处理逻辑非常一刀切。它捕获到了这个底层移动错误,但它没有意识到是跨盘问题,而是粗暴地认为“下载不完整/解压失败”。于是,它把真实的 EXDEV 错误吞掉,抛出误导性极强的 Failed to extract executable,并且为了防止缓存污染,顺手触发回滚机制,把你刚刚下载好的文件全删了

这就完美解释了为什么公司电脑(项目和系统全在 C 盘)没有问题,而家里的电脑(项目在 D 盘)无限死循环。


3. 如何解决?

既然知道了核心矛盾在于**“不能跨盘符 rename”**,解决思路很简单:我们在 C 盘原地让它下载并缓存一次

如果你使用的是 Git Bash,请直接执行以下命令:

# Step 1: 强行切换到 C 盘家目录
cd ~

# Step 2: 随便建个临时测试脚本
echo "console.log('hello')" > test.js

# Step 3: 直接调用 D 盘项目里的 Bun 二进制 或者全局的 Bun,但在 C 盘工作环境下触发编译
# 这样 Bun 会在 C 盘下载、C 盘解压、C 盘重命名移动到 .bun/install

# 注入 Intel Mac 缓存
/d/workspace/wx-shortlink-tool/node_modules/bun/bin/bun.exe build ./test.js --compile --target=bun-darwin-x64 --outfile=test-mac-x64

# 注入 M 芯片 Mac 缓存
/d/workspace/wx-shortlink-tool/node_modules/bun/bin/bun.exe build ./test.js --compile --target=bun-darwin-aarch64 --outfile=test-mac-aarch64

# Step 5: 重新回到 D 盘的项目里
cd /d/workspace/wx-shortlink-tool

# Step 6: 重新执行项目的打包脚本
node 你的脚本名字.js

4. 总结与吐槽

作为前端领域的新星,Bun 的速度确实快如闪电,但在 Windows 生态的底层细节处理上,确实还带着不少开源工具特有的“粗糙”。

  • 反思:永远不要轻信不讲道理的报错信息(比如把跨盘连接失败报成网络不完整)。
  • 教训:遇到诡异的文件丢失或假死,多去 GitHub Issue 刨一刨,指不定就是哪个底层核心开发者拍脑袋写的 try-catch 把你给坑了。