You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
在 Windows 上,将目录选择器固定为 browse 交互(网页内目录浏览框)后,点击进入用户主目录下的旧版 junction 目录(如 C:\Users\<用户名>\Application Data、C:\Users\<用户名>\Cookies 等)会直接报错:
cannot list C:\Users\beta\Application Data: EPERM: operation not permitted, opendir 'C:\Users\beta\Application Data'
cannot list C:\Users\beta\Cookies: EPERM: operation not permitted, opendir 'C:\Users\beta\Cookies'
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
一、问题概述(Summary)
在 Windows 上,将目录选择器固定为
browse交互(网页内目录浏览框)后,点击进入用户主目录下的旧版 junction 目录(如C:\Users\<用户名>\Application Data、C:\Users\<用户名>\Cookies等)会直接报错:这些目录是 Windows 为兼容旧程序保留的 junction(reparse point),分别指向
AppData\Roaming、AppData\Roaming\Microsoft\Windows\Cookies等真实目录,普通用户完全可以访问(资源管理器、cmd 均可正常进入)。但 Node 的fs.opendir/readdir对 junction 路径本身直接返回EPERM。二、复现步骤(Steps to Reproduce)
Application Data、Cookies、Local Settings、My Documents、Recent等)。@deepseek-ai/dsh-host-directory-picker-browse+@deepseek-ai/dsh-client-ui-directory-picker-browse;即dsh-host-directory-picker-auto解析为browse,或直接在配置中固定 browse)。Application Data(或Cookies、Local Settings等任一 junction 行)。实际行为:对话框顶部路径区显示
cannot list <junction路径>: EPERM: operation not permitted, opendir '<junction路径>',无法进入该目录。期望行为:与资源管理器一致,正常进入 junction 指向的真实目录(或至少不报 EPERM)。
三、根因分析(Root Cause)
3.1 Node 对 junction 路径的行为
在本机验证(Node v24.14.1):
C:\Users\beta\Application Data的结果fs.statisDirectory() === true)fs.realpathC:\Users\beta\AppData\Roamingfs.readdirfs.opendirfs.opendir(realpath(...))即:Node(libuv)对 Windows junction 路径的目录打开操作返回 EPERM,而路径解析(stat/realpath)正常。这属于 libuv/Node 在 Windows 上对 reparse point 的处理行为(junction 虽在
Dirent中表现为isSymbolicLink() === true,但按链接路径打开目录仍被拒绝)。3.2 出错的代码位置
@deepseek-ai/dsh-host-directory-picker-browse的BrowseDirectoryPicker.list()(lib/index.js):外层 catch 把它包装为:
于是客户端只看到
cannot list ... EPERM ...,没有任何可操作提示。3.3 影响范围
Application Data、Cookies、Local Settings、My Documents、Recent、SendTo、Start Menu、Templates等十几个旧版 junction 全部受影响)。C:\Users\Public\Documents等)同样受影响。四、建议修复(Suggested Fix)
方案(已本地验证):EPERM 时经
realpath解析后重试在
list()中,当opendir因EPERM失败且平台为 win32 时,先realpath解析 junction 到真实路径,再用真实路径重试一次;成功后列表行、面包屑、返回的path均使用解析后的真实路径(后续导航也不会再次踩到 junction)。补丁(对
dsh-host-directory-picker-browse/lib/index.js):try { const opening = opendir(target); - const level = await raceAbort(opening, signal).catch((error) => { + const level = await raceAbort(opening, signal).catch(async (error) => { opening.then((dir) => dir.close().catch(swallowCloseFailure), () => {}); - throw error; + // Windows legacy junctions (e.g. `C:\Users\<name>\Application + // Data` → `AppData\Roaming`) refuse directory opens through the + // junction path with EPERM; resolve to the real target and retry + // once so the level still lists (and rows/crumbs carry the real + // path, which also opens cleanly on later navigation). + if (process.platform !== "win32" || error?.code !== "EPERM") throw error; + let resolved; + try { + resolved = await realpath(target); + } catch { + throw error; + } + if (resolved === target) throw error; + target = resolved; + const retry = opendir(target); + return raceAbort(retry, signal).catch((retryError) => { + retry.then((dir) => dir.close().catch(swallowCloseFailure), () => {}); + throw retryError; + }); });备选方案(更彻底)
list()入口对 win32 的 target 先做一次realpath规范化(所有路径都解析 junction),UI 行为一致且无重试路径;代价是多一次 syscall,且resolve不存在的路径时 realpath 会失败(需回退)。dsh-host-directory-picker)约定:所有 Windows 路径统一走realpath后的规范形式。修复验证
本地用修补后的包直接调用
list(),三组用例均通过:注意:本地补丁改的是已安装包文件,dsh 升级会被覆盖,所以希望官方在下一版本中合入该修复。
All reactions