fix: 修复打开 WSL 文件时主进程因 EISDIR 崩溃 (#172)#236
Merged
LarryZhu-dev merged 1 commit intoJun 7, 2026
Merged
Conversation
打开 \\wsl.localhost\ 下的文件时,应用会自动把文件所在目录作为工作区并用 chokidar 递归监听。chokidar 遍历到目录中的 Linux 软链接(如 *.so.0)时会对其 lstat,而 Windows/Node 对经 9P 重定向器暴露的 WSL 软链接做 lstat 会返回 EISDIR (底层是 9P 不支持读取该软链接的 reparse 数据,返回 Incorrect function,被 libuv 映射成了 EISDIR)。该错误没有任何地方处理,冒泡成主进程 uncaughtException,于是弹出 "A JavaScript error occurred in the main process" 并使文件区卡死。 修复采用双保险: - workspacePath.ts:isRemoteWorkspacePath 不再依赖 isWindows 做前置短路。该模块运行在 渲染进程(nodeIntegration:false),那里 process.platform 不可用,isWindows 恒为 false,会让原有的 WSL/远程路径防护永远失效。去掉该短路后,防护真正生效,从源头避免对 WSL 目录自动加载工作区与递归监听。UNC 路径(\\wsl.localhost\、\\wsl$\、\\server\share) 本身是 Windows 专有写法,不会出现在其他平台的合法路径里,无需平台判断即可安全识别。 - ipcBridge.ts:给 file watcher 与 directoryWatcher 各补一个 "error" 监听。chokidar 出错时会 emit "error",没有监听器时会冒泡成主进程 uncaughtException。补上后任何 watcher 错误都只记录、不再使应用崩溃,对所有文件系统(不止 WSL)都更健壮。 注:本次修复保证不再崩溃,但 WSL 文件暂时不会自动加载左侧文件树(防护跳过了)。后续会做 第二层改进,让 WSL 文件也能正常显示文件树。
LarryZhu-dev
pushed a commit
that referenced
this pull request
Jun 7, 2026
* fix: 修复打开 WSL 文件时主进程因 EISDIR 崩溃 (#172) 打开 \\wsl.localhost\ 下的文件时,应用会自动把文件所在目录作为工作区并用 chokidar 递归监听。chokidar 遍历到目录中的 Linux 软链接(如 *.so.0)时会对其 lstat,而 Windows/Node 对经 9P 重定向器暴露的 WSL 软链接做 lstat 会返回 EISDIR (底层是 9P 不支持读取该软链接的 reparse 数据,返回 Incorrect function,被 libuv 映射成了 EISDIR)。该错误没有任何地方处理,冒泡成主进程 uncaughtException,于是弹出 "A JavaScript error occurred in the main process" 并使文件区卡死。 修复采用双保险: - workspacePath.ts:isRemoteWorkspacePath 不再依赖 isWindows 做前置短路。该模块运行在 渲染进程(nodeIntegration:false),那里 process.platform 不可用,isWindows 恒为 false,会让原有的 WSL/远程路径防护永远失效。去掉该短路后,防护真正生效,从源头避免对 WSL 目录自动加载工作区与递归监听。UNC 路径(\\wsl.localhost\、\\wsl$\、\\server\share) 本身是 Windows 专有写法,不会出现在其他平台的合法路径里,无需平台判断即可安全识别。 - ipcBridge.ts:给 file watcher 与 directoryWatcher 各补一个 "error" 监听。chokidar 出错时会 emit "error",没有监听器时会冒泡成主进程 uncaughtException。补上后任何 watcher 错误都只记录、不再使应用崩溃,对所有文件系统(不止 WSL)都更健壮。 注:本次修复保证不再崩溃,但 WSL 文件暂时不会自动加载左侧文件树(防护跳过了)。后续会做 第二层改进,让 WSL 文件也能正常显示文件树。 * docs: 新增 WSL/远程文件监听第二层实施规约 (草案 v2) * docs(wsl-l2): 记录 §0 前置实测结果(A/B PASS) + 按实测修订 symlink 分支 §0 实测 2026-06-06 (Debian11 / Node v22 / Windows host): A 完整建树无 EISDIR -- PASS: - 生产 scanDirectory 在含 symlink/.so链/大目录/深目录 的 WSL 树上建 115 节点, 0 吞错 / 0 EISDIR / 0 uncaught - 新发现: WSL symlink 从 Windows 侧完全读不透 lstat/readlink=EISDIR, stat(跟随)/readFile=ENOENT (含指向真实存在的目标) -> symlink 仅能由 readdir d_type 识别, 拿不到 mtime, 无法解引用/打开 B fs.watchFile 在 9P 可靠性 -- PASS (全绿): 追加 / 原子替换(mv,新inode) / 同mtime仅改size(size触发) / 删除(mtime=0) / 删后重建(跟踪存活) / 同秒多写 全部 DETECTED 据 A 缩范围 (spec §4.3/§6.2/§8): - WSL 分支默认丢弃 symlink (避免死节点/点开 ENOENT), 重指向不可知文档化为局限 - isSymbolicLink() 分支只对 local/SMB 有效 - WSL symlink inert 显示移入 §8 defer * feat(wsl-l2): WSL/远程文件树轮询监听(撤销第一层跳过) 第二层:撤销第一层对 WSL/远程路径的"跳过自动加载工作区",让 WSL 文件也能 显示文件树并在外部变更时提示重载。事件式 fs.watch 在 9P 上启动即 EISDIR, 故 WSL 改走轮询;本地/SMB 保持 chokidar 实时。依据 §0 前置实测(已 PASS)。 新增 src/main/wslWatch.ts(不依赖 electron,回调注入,便于单测): - 路径分流 classifyWatchTarget / normalizeWatchPath(还原 \\?\UNC\ 防误判重开 EISDIR) - scanDirectory(WSL 丢弃读不透的 symlink;本地/SMB 加 isSymbolicLink 分支) - WslDirectoryWatcher:per-dir interval + in-flight guard + epoch 丢弃迟到结果 + 快照(path+mtime+size)diff + 不可达不清树 + 300ms debounce - WslFileWatcher:per-window union 引用计数 + fs.watchFile + mtime=0 删除语义 + size 兜底 + focus checkWindow 兜底 + before-quit dispose 接入 ipcBridge.ts:watchDirectory/file:watch 按 classify 分流;getDirectoryFiles 改用模块 scanDirectory;app browser-window-focus / before-quit / 窗口关闭清理。 renderer:workspacePath.shouldAutoLoadWorkspace 撤销远程跳过;useWorkSpace 远程 空树也置锁(避免 tabs 变化反复重扫慢 9P) + refreshWorkSpace 加 in-flight 互斥。 测试:node:test 22 个(纯函数 + 两个管理器,假依赖) 全过;另用真实 fs.watchFile + snapshotDirectory 在真 \\wsl.localhost\ 集成验证 9/9(目录增删/文件追加替换/focus/ 引用计数)。新增 pnpm test 脚本。 注:GUI 视觉确认(文件树渲染 / 重载弹框)需本机 pnpm run dev 验证。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
修复 #172:在 Windows 上打开 WSL(
\\wsl.localhost\)里的文件时,文件区(Explorer)卡死并不停弹出:根因
打开文件时,应用会自动把文件所在目录作为工作区,并用 chokidar 递归监听(
workspace:watchDirectory,depth:10)。chokidar 遍历到目录里的 Linux 软链接(例如librockchip_mpp.so.0这种.so版本软链接)时会对它lstat。而 Windows/Node 对经 9P 重定向器暴露的 WSL 软链接做
lstat会返回EISDIR——底层其实是 9P 不支持读取该软链接的 reparse 数据,返回Incorrect function(ERROR_INVALID_FUNCTION),被 libuv 映射成了EISDIR,所以错误信息里的 "directory" 是个误导(它跟目录无关)。这个错误没有任何地方接住:chokidar emit 的
error事件没有监听器,主进程也没有uncaughtException处理,于是冒泡成未捕获异常并反复弹框。另外,项目其实已经有一道针对 WSL/远程路径的防护
shouldAutoLoadWorkspace,但它依赖isWindows,而isWindows由getPlatform()读process.platform得出——这段代码运行在渲染进程(nodeIntegration:false),那里没有 Node 的process,isWindows恒为false,导致这道防护从未真正生效。修复(双保险)
workspacePath.ts:isRemoteWorkspacePath去掉!isWindows前置短路。UNC 路径(\\wsl.localhost\、\\wsl$\、\\server\share)本身是 Windows 专有写法,不会出现在其他平台的合法路径里,无需平台判断即可安全识别。去掉短路后防护真正生效,从源头避免对 WSL 目录自动加载工作区与递归监听。ipcBridge.ts:给 file watcher 与 directoryWatcher 各补一个.on("error")。chokidar 出错时会 emiterror,没有监听器就会冒泡成主进程 uncaughtException;补上后任何 watcher 错误只记录、不再使应用崩溃,对所有文件系统(不止 WSL)都更健壮。影响 / 后续
本次修复保证不再崩溃,文件可正常打开、编辑、保存。代价是 WSL 文件暂时不会自动加载左侧文件树(防护把它跳过了)。
后续会做第二层改进,让 WSL 文件也能正常显示文件树——思路是改用
readdir(在 WSL 上正常,且能识别软链接而不对其lstat)配合轮询刷新,绕开会对每个软链接lstat的 chokidar。该改进会作为后续单独的 PR 提交。