根本原因是Node.js进程无权限读写系统临时目录或临时文件未创建,需用os.tmpdir()动态获取路径并测试写权限,排查macOS自动清理、Windows组策略/杀软拦截、umask未生效及跨进程生命周期问题。

为什么 fs.readFile('/tmp/config.json') 在 VSCode 终端里报 ENOENT 或 EACCES
根本不是路径写错了,而是 Node.js 进程没权限读写系统临时目录,或者临时文件根本没被创建出来。VSCode 终端继承的是 shell 的权限上下文,但 macOS/Linux 的 /tmp 和 Windows 的 %TEMP% 有各自严格的沙盒策略:macOS 对 /tmp 下的文件做自动清理(7天),Windows 的 %TEMP% 可能被组策略锁定、杀软拦截,或由当前用户 profile 隔离。
- 先确认临时路径是否真实可写:
console.log(os.tmpdir()),再用fs.accessSync(os.tmpdir(), fs.constants.W_OK)测试写权限 - 别硬编码
/tmp—— 用os.tmpdir()动态获取,它在 Windows 返回C:\Users\XXX\AppData\Local\Temp,不是C:\Windows\Temp - 如果
fs.writeFileSync(path.join(os.tmpdir(), 'test.txt'), 'ok')报EACCES,说明进程没拿到该目录的写入权,不是 Node 版本问题
VSCode 终端启动时未加载用户级 umask 或 SELinux 上下文
Linux/macOS 下,VSCode 默认以 non-login shell 启动终端,跳过 ~/.profile 或 /etc/profile 中设置的 umask,导致新建文件默认权限是 0600(只读 owner),其他进程(比如另一个 Node 实例)就打不开它。
- 在 VSCode 终端执行
umask,对比系统终端输出;若不同,说明权限掩码没生效 - 临时修复:在
settings.json中加"terminal.integrated.shellArgs": ["-l"],强制走 login shell(重启 VSCode 生效) - 长期方案:把
umask 0002写进~/.zshrc或~/.bash_profile,并确保 VSCode 是从 Dock 或桌面快捷方式启动(非命令行code)
Windows 上 %TEMP% 被 OneDrive、Defender 或组策略重定向
尤其在企业环境,%TEMP% 常被重定向到 %USERPROFILE%\AppData\Local\Packages\... 或 OneDrive 同步目录,而 VSCode(尤其非管理员运行时)无法写入这些受保护路径,fs.openSync 直接抛 EACCES。
- 查真实路径:
echo %TEMP%(CMD)或$env:TEMP(PowerShell),再看该路径是否在 OneDrive 文件夹内 - 检查 Windows 安全中心 → “应用控制” → “勒索软件防护”是否锁定了该路径
- 临时绕过:在脚本开头加
process.env.TEMP = path.join(os.homedir(), 'temp'),然后fs.mkdirSync(process.env.TEMP, { recursive: true })
临时文件被提前清理或跨进程访问失败
Node.js 的 fs.unlink 不保证立即释放磁盘,尤其在 macOS APFS 或 WSL2 下,另一个进程可能因文件句柄未完全关闭而读不到内容;更常见的是,你用 fs.mkdtemp 创建的目录,在父进程退出后被系统回收,子进程再去读就报 ENOENT。
- 别依赖“刚写完就立刻读”:加
await new Promise(r => setTimeout(r, 10))等最小延迟,或用fs.promises.stat轮询确认文件存在 - 用
fs.promises.mkdtemp(path.join(os.tmpdir(), 'myapp-')),而非拼字符串——mkdtemp自动处理唯一性和权限 - 跨进程场景(如 spawn 子进程读临时文件),务必传绝对路径,且确认子进程继承了同个
os.tmpdir()值
mkdtemp,但子进程在 WSL2 里跑,路径没转换。排查时每次只动一个变量,否则连哪一层崩了都分不清。


















