DeepSeek Harness 沙箱命令全失败:workspace-write 的 temp 根与管道 stdio

故障排查发布于 2026-10-17作者: DeepSeek Plugin 插件市场
DeepSeek HarnessDSH 沙箱workspace-writespawn EPERM0xC0000142Windows ACLtemp root
Windows 上 DeepSeek Harness 的 workspace-write 命令全失败,症结在沙箱 ACL 授权链:temp 根落在工作区内、管道 stdio 被拒、整树 ACL 重写让宿主无响应;本文给出逐条判据、TMP 与恢复步骤。

在 Windows 上把 DeepSeek Harness(DSH)的文件策略设为 workspace-write 之后,如果命令不是「跑错」,而是「根本没跑起来」——echo 都失败、输出重定向成 0 字节空文件、子进程直接 0xC0000142、或宿主突然整机无响应——那问题几乎一定在沙箱的 ACL 授权链上,而不是你的命令。 这条链在不同的前提缺失下会碎成五种样子:临时根落在工作区里会直接报 Windows ACL temp root must be outside the workspace;受限令牌建不了匿名管道会把输出捕获变成静默丢数据;受限进程用管道 stdio 起孙进程会 spawn EPERM;已授权根缺了三件套之一会触发整树 ACL 重写、把宿主事件循环焊死;工作区根若是 UNC/9P 虚拟盘则根本不支持 ACL 授权。本文把这五种按「报错文本 → 判据 → 可粘贴命令」拆开,末尾还给一个把受限会话里最常踩的坑一次性排掉的检查清单。

DeepSeek Harness 沙箱 workspace-write 命令执行器整体失效:temp 根必须在工作区之外

一句话结论:沙箱给受限子进程准备的临时根必须是「工作区之外」的目录,而它默认取系统 TEMP;一旦你的工作区把 TEMP 包了进去,沙箱在组装 runner 参数时就会断言失败,命令执行器整体停摆,文件工具却照常可用。 报错形态非常直白:

Error: Windows ACL temp root must be outside the workspace: C:\Users

据讨论中用户实测,这条约束来自 sandbox-windows-acl 的路径边界检查,它在 materializeAclGrant 的第一句执行——也就是在 spawn 任何子进程之前,所以你换命令、换 shell、换语法都没用(#9175)。

DSH插件 为什么「工作区包含 TEMP」这么容易撞上

因为 Windows 上最常见的两个工作区选择——用户主目录和整个盘根——天然把 TEMP 包在里面。 判定用的是 realpathSync.native 做的包含关系,不是字符串前缀,所以下面这些工作区都会命中:

  1. 工作区 = C:\Users\<你>(TEMP 是 C:\Users\<你>\AppData\Local\Temp,被包含);
  2. 工作区 = C:\Users(整个用户目录树);
  3. 工作区 = C:\(盘根,几乎所有临时目录都在里面);
  4. 任何把 TMP / TEMP 环境变量指到工作区内部的配置。

讨论里同时给了一个重要的下一层提示:这条规则本身是有意的 capability 隔离,不是缺陷;真正可改进的是失败时的降级方式与报错可操作性——现在的报错只告诉你「必须在工作区外」,没告诉你改哪个变量(#9175)。

DeepSeek Harness 编号处置:把 temp 根搬出工作区

修法是把 TMP / TEMP 指到工作区外的固定目录,而不是去改工作区。 步骤如下:

  1. 先确认当前 temp 根落在哪。在非受限的 PowerShell 里执行:
powershell
[System.IO.Path]::GetTempPath()
#   → 例如 C:\Users\<你>\AppData\Local\Temp\
  1. 把结果与工作区路径做包含判断。若工作区是 C:\Users\<你>、而输出落在它下面,即命中本条。工作区可以用 设置 → 工作区 里显示的路径核对。
  2. 建一个明确在工作区外的临时目录,并把它设为用户级 TMP / TEMP:
powershell
New-Item -ItemType Directory -Force D:\dsh-temp | Out-Null
setx TMP  "D:\dsh-temp"
setx TEMP "D:\dsh-temp"
  1. 完全退出 DSH 再重开(setx 只影响新进程,正在跑的宿主不会继承)。
  2. 重开会话后在 workspace-write 下跑一条最小命令验证:
powershell
pwsh -Command "echo hi"
#   → 预期输出 hi,不再报 must be outside the workspace
  1. 回滚:把 TMP / TEMP 删回用户默认即可,setx TMP "" 后重启会话。

一个必须记住的连带现象:把工作区设成 C:\Users 这类系统属主目录时,改完 TMP 只是让它从「temp 根报错」走到「下一层 ACL 授权失败」,因为 C:\Users 及其兄弟目录的属主与 DACL 都不满足 grantWrite 前提。这类工作区建议整体换掉,别在原地硬修(#9175)。

DSH插件 会话内临时目录失效:同一会话所有命令持续失败、不自愈

还有一种「命令执行器整体失效」不是环境变量引起的,而是沙箱缓存了一个已经消失的临时目录。 症状是:会话开头几条命令还正常,之后所有命令(含只读命令)都失败,重启 DSH 才能恢复(#9322)。

据社区逐行核对源码,链路是这样的:

  1. 沙箱在会话内为临时能力建了一张缓存表,命中缓存后直接返回,没有再做一次「目录是否还在」的复验;
  2. 于是缓存里的那个已失效路径被原样下发给 runner;
  3. runner 在启动时对 --temp 目录做必需性检查,找不到目录即退出,退出码 127 并在 stderr 带 windows-acl-run: 前缀;
  4. 缓存表只在 provider dispose 时清空,所以同一会话内不会自愈。

关于「目录为什么会消失」,讨论里如实标注为未定论——代码里没有删除这条路径的地方,最可能是 DSH 之外的进程(清理工具、杀软隔离、临时目录清理策略)动了它(#9322)。

处置办法(按代价从小到大):

  1. 重启 DSH——会重建新的临时路径与 SID,一步恢复;
  2. 应急时把该会话切成 read-only(只读档不建可写根,也不依赖那份临时能力);
  3. 长期避免:把工作区与系统临时目录清理策略错开,别让第三方清理器扫到 DSH 会话临时目录。

DeepSeek Harness UNC / 虚拟盘根上 ACL 沙箱不支持

工作区根在 UNC 或 9P/SMB 虚拟盘上时,workspace-write 不是「配不对」,而是「这类根不支持」。 症状是 run_code / PTC worker 启动即失败,报 GetNamedSecurityInfoW failed (Win32 1),却在 UI 上被归成 worker-exit,看起来像 worker 崩了(#9315)。

据报告者与社区核对源码:9P 重定向器不提供 Windows 安全描述符,读取当前安全信息返回 ERROR_INVALID_FUNCTION;而 Windows 平台上沙箱只有一个 windows-acl 后端,没有 UNC 分支。这里的诉求应该是「把该根归类为不支持沙箱」,而不是继续报 worker-exit(#9315)。

处置步骤:

  1. 确认工作区根是不是 \\wsl.localhost\...、\\wsl$\...、网络共享或云盘挂载点;
  2. 切该会话到 read-only,预期 PTC 立即恢复(只读档不读 ACL);
  3. 或先在资源管理器把这个网络位置映射成盘符(Z:\...),再用盘符路径重建工作区;
  4. 两者都不想动时,改用 danger-full-access 只作为临时手段,不建议长期使用。

DSH插件 受限沙箱内 CIM/WMI 整体不可用,且失败会被静默成 0

最后一条不报错、只给错值,最容易被误判成「环境正常但数据不对」。 在受限令牌沙箱内,Get-CimInstance 及全部 CIM 派生命令整体不可用,返回 0x80041003;更麻烦的是 Get-PSDrive 会把 Used/Free 报成 0,而且加上 -ErrorAction SilentlyContinue 之后拒绝会变成空结果(#9272)。

据讨论中实测与源码核对:受限令牌缺少让 root/cimv2 命名空间安全检查通过的身份(Authenticated Users 不在两趟检查的允许列表里);失败信息走的是命令的 stderr 而不是 PowerShell 的错误流,部分命令干脆返回静默的默认值——这类「失败不提示」的静默拒绝与工具调用永久卡死,另见 DeepSeek Harness 工具调用永久卡死。

判读规则与替代命令(实测可用的替代路线):

  1. 先记住一条判读规则:沙箱内 CIM 命令的空结果和 0 值都不算证据,别据此判断磁盘满、网卡断、进程不存在。
  2. 网络连接改用 netstat -ano 替代 Get-NetTCPConnection。
  3. 进程与服务改用 Get-Process、Get-Service 替代 Get-CimInstance Win32_Process / Win32_Service。
  4. 磁盘容量改用 [System.IO.DriveInfo]::GetDrives() 替代 Get-PSDrive 的 Used/Free。
  5. 网卡与路由改用 ipconfig /all、netsh、route print、arp -a;性能计数改用 Get-Counter;注册表读取不受影响。

DeepSeek Harness 管道 stdio 被拒:0xC0000142、0 字节空文件与 spawn EPERM

一句话结论:受限令牌下「给子进程接管道」这件事本身会被 Windows 拒绝,所以凡是需要捕获原生命令输出的写法都会失败——轻则输出静默丢成 0 字节文件,重则子进程在加载器阶段就死掉(0xC0000142),或者 libuv 直接抛 spawn EPERM。 这一族最坑的地方是失败被伪装成别的问题:框架会把它渲染成 [sandbox: file access denied …],方向一下就带偏了(#9857)。

DSH插件 触发与不触发:一条单变量对照就能定性

判据只有一条变量——输出到底有没有被「捕获」。 对照表:

写法是否接管道预期结果
pwsh -Command "echo hi" 直接打屏否正常输出
pwsh -Command "echo hi" > out.txt是(重定向)out.txt 为 0 字节,或子进程 0xC0000142
$x = pwsh -Command "echo hi"是(赋值捕获)同上
npm run build 2>&1 | tee log是(合并流 + 管道)spawn EPERM 或静默丢失
工具自己把输出写进会话日志(不重定向)否正常

据报告者的 A/B/C/D 四组对照与社区逐行核对源码:受限/低完整性令牌下无法创建匿名管道(AnonymousPipeServerStream 被拒),而具名管道正常——这也是为什么 PowerShell 自己的 | 管道有时能用、而 .NET 走 ProcessStartInfo 捕获输出就必死:后者走的是另一条安全描述符路径,setTokenDefaultDaclGrant 那次合并 ACE 覆盖不到它(#9857)。

DeepSeek Harness 编号排查:三步确认是管道问题而不是命令问题

先做对照,再改命令。 三步:

  1. 找到一条失败的命令,把它的输出重定向去掉,改成直接打屏,重跑。若立刻正常 → 定性为管道捕获问题。
  2. 再确认失败时的真实退出码。在 DSH 会话里跑 cmd /c "pwsh -Command \"echo hi\" > %TEMP%\o.txt & echo exit=%errorlevel%",预期看到 exit=-1073741502(即 0xC0000142)。
  3. 检查那个被重定向的文件:Get-Item %TEMP%\o.txt | Select Length,长度是 0 即「输出被静默丢弃」这一格。

DSH插件 处置办法:三条可用路线

  1. 首选——不捕获输出。让命令把结果直接写进会话日志或写进工作区文件,用 read 工具去看,不要在受限模式里做重定向与赋值捕获。
  2. 跑测试/构建类命令时用 inherit 或 ignore stdio,别用 pipe。据讨论中社区引用的包内 README,这是文档化的已知边界:受限令牌下命名管道客户端要请求写访问,而 restricting SID 没有被授予该权限,这一步是 Win32 默认安全描述符模板决定的,改令牌默认 DACL 也解决不了命名管道(#9549)。
  3. 需要完整跑通 CI 类脚本时,把这一步放到不受限的外层执行(例如在宿主终端里跑),DSH 只负责读产物。

顺带一个省时间的区分:spawn EPERM 有的是「受限进程起孙进程的管道问题」(本文这条),有的是「沙箱 runner 自己没起来」——后者的特征是 stderr 带 windows-acl-run: 且退出码 127。看到这个前缀就别在业务命令上找了(#9549)。

DeepSeek Harness ACL 整树重写:宿主无响应但进程不崩

一句话结论:当某个已授权根缺少「三件套」里的任意一项时,沙箱会退化成对整棵树做一次急切的 SetNamedSecurityInfoW,而这次调用是在宿主进程内同步执行的——期间事件循环被占满,127.0.0.1:3080 自然无响应,所有会话停止写入,但进程不会崩、也不会有任何报错日志。 这是本族里唯一「看起来像死机」的一条(#9818)。

据社区核对源码,判据与放大点如下:

  1. 幂等短路要求同一个根同时具备:capability 允许 ACE、Everyone:(CI)(DENY)(DC) 的 world 删除拒绝,以及 Low 完整性标签;
  2. 缺任意一条即走合并路径 mergeAndApply,对整棵树做急切传播;
  3. 调用点在宿主进程内且是同步的,没有让出事件循环;
  4. 放大点在于升级:较早版本只授过 capability ACE,world 删除拒绝与 Low 标签是后续引入的,因此升级用户会在下一次供给时触发一次整树重写。

讨论中给了一个量级参考:D:\projects 这种体量的工作区,整树传播实测约 4490 秒(约 75 分钟),期间宿主全程无响应(#9818)。

DSH插件 编号处置与预防

  1. 先确认是不是它:接口无响应期间,Get-Process 看宿主进程是否仍在、CPU 是否被单核打满;配合 icacls "<工作区>" 观察是否正在被批量写入标签。
  2. 短期恢复:等它跑完,或重启宿主。不要在这期间反复点 UI 触发新的供给。
  3. 预防——先补齐三条件让幂等短路命中:确认根上同时存在 capability 允许 ACE、world 删除拒绝与 Low 标签,缺的那个补齐后再把工作区交给 DSH;这套 Low 标签自身的副作用与撤销方法,另见 DeepSeek Harness 沙箱 Low 完整性标签。
  4. 避免让单个工作区过大;大工作区建议拆成多个较小的工作区分别连接。
  5. 版本提示:据社区核对,0.2.0-rc.2 与后续 alpha 的两个 ACL 包哈希一致,该行为尚未修;「把整树传播移出事件循环」属于核心侧改动,插件层只能观测、无法规避(#9818)。

DSH插件 排查注意事项

五条症状同属「Windows ACL 沙箱前提不满足」一族,但触发点各不相同,按报错文本分流最快,别一上来就改 ACL。 九条要点:

  1. 先读报错文本:must be outside the workspace 是 temp 根位置;windows-acl-run: + exit 127 是 runner 启动失败;spawn EPERM 是管道 stdio;0xC0000142 是子进程加载器初始化;Win32 1 是 UNC 根不支持;只有 Win32 5 才是经典的 ACL 授权失败。
  2. 文件工具正常、只有 shell 失败,几乎可以确定问题在沙箱初始化而非命令本身——文件工具走的是另一条路径(#9175)。
  3. TMP / TEMP 是唯一杠杆:改完必须完全退出 DSH 再重开,setx 不影响已在运行的宿主。
  4. 不要用 C:\、C:\Users、用户主目录当工作区:这三类会让 temp 根与 ACL 授权两条前提同时不满足,属于自找麻烦。
  5. 重定向与赋值捕获是禁忌:受限模式里凡是被捕获的输出都可能变成 0 字节或直接触发 0xC0000142。
  6. inherit / ignore stdio 是文档化边界内的解法,不是绕过安全控制——改令牌默认 DACL 解决不了命名管道(#9549)。
  7. 宿主无响应时不要急着重启:先看是不是整树重写;75 分钟量级的中途重启会让下一次供给从头再来。
  8. 沙箱内的 CIM 结果不可信:空值与 0 值要当成「被拒绝」而不是「真的没有」(#9272)。
  9. 修复状态如实:截至 0.2.0-rc.2 前后,上述五条都仍在复现,属已知边界;社区另有诊断类插件(如 @argszero/cordis-plugin-sandbox-grant-advisor)能把部分拒绝签名翻译成可操作的下一步,但它不替你改 ACL、也不提权。

这类系统级排查,反复在终端里翻 icacls 输出和进程状态很费时间。如果你更想把插件的安装、更新、卸载与系统日志收在一个界面里管,与其在各处手敲命令,不如用 DSH Plugin Hub 这类可视化插件市场——插件装卸、更新确认、诊断与日志都在一个面板里,排环境问题时能少绕几步。

已安装插件列表

来源:Discussion #9175、Discussion #9322、Discussion #9857、Discussion #9549、Discussion #9818、Discussion #9315、Discussion #9272。

常见问题

DeepSeek Harness 的 workspace-write 下每条命令都报 Error: Windows ACL temp root must be outside the workspace,连 echo 都跑不了,是什么原因?

DeepSeek Harness 的 workspace-write 报 Windows ACL temp root must be outside the workspace,是沙箱的 capability 隔离规则在拒绝,而不是命令写错了。沙箱给受限子进程准备的临时根取的是系统 TEMP(os.tmpdir()),它在组装 runner 参数时就会先断言「临时根必须位于工作区之外」;一旦你的工作区包含了 TEMP 所在目录(典型是工作区设成 C:\Users),断言在启动任何子进程之前就抛出,所以连 echo test 也失败。修法是把 TMP 与 TEMP 指到工作区外,例如 setx TMP D:\dsh-temp,重开 DSH 会话即可。

为什么 DeepSeek Harness 的 workspace-write 下把命令输出重定向到文件会得到 0 字节空文件,或者子进程直接以 0xC0000142 退出?

DeepSeek Harness 的 workspace-write 下把命令输出重定向到文件得到 0 字节空文件、或子进程直接以 0xC0000142 退出,根因是受限令牌下匿名管道创建被拒。当 Python 的 print 被 stdout 重定向、或者你用 $x = pwsh -c ... / 2>&1 / > 去接住输出时,Windows 需要为子进程建立匿名管道,而低完整性受限令牌没有创建它所需的权限,于是子进程要么加载失败(STATUS_DLL_INIT_FAILED,0xC0000142),要么输出被静默丢弃、只留下一个 0 字节文件。最快的规避是在受限模式里避免捕获原生命令输出,让它直接打到会话日志。

为什么 DeepSeek Harness 的 workspace-write 里跑 npm test 或 vite 报 spawn EPERM,而同一条命令在沙箱外却正常?

DeepSeek Harness 的 workspace-write 里 npm test 或 vite 报 spawn EPERM,是受限于命名管道 stdio,属于沙箱的已知边界而不是 npm 的问题。libuv 为 stdio: 'pipe' 起子进程时要打开命名管道并请求写访问,受限令牌没有被授予任何 restricting SID,Windows 默认安全描述符直接把这一步拒掉,于是以 spawn EPERM 抛出,测试体其实从未启动。可用的做法是把测试放到不受限的外层执行,或让子进程改用 stdio: 'inherit' / 'ignore';注意单个 bad 命令不会静默,它一定会在 stderr 里带 windows-acl-run: 前缀。

给 DeepSeek Harness 加了 workspace-write 之后 Windows 宿主突然 127.0.0.1:3080 无响应、所有会话不再写入,进程还在但没有任何报错,怎么恢复?

DeepSeek Harness 加了 workspace-write 之后 Windows 宿主 127.0.0.1:3080 无响应、会话不再写入,大概率是撞上了 ACL 整树重写。当某个已授权根缺少三件套(capability 允许 ACE、Everyone 的 world 删除拒绝 ACE、Low 完整性标签)里的任意一项时,沙箱会退化成对整棵树做一次急切的 SetNamedSecurityInfoW,而这一步是在宿主进程内同步执行的,事件循环被占满期间 HTTP 服务自然不响应。实测过一个 D:\projects 量级的工作区整树传播耗时约 75 分钟。恢复办法是等它跑完或重启宿主,长期做法是把工作区根先补齐三条件,让幂等短路命中。

把 \\wsl.localhost\ 这类 UNC 路径当 DeepSeek Harness 的工作区根,跑 run_code 时报 GetNamedSecurityInfoW failed (Win32 1),能修吗?

把 UNC 路径当 DeepSeek Harness 的工作区根时 run_code 报 GetNamedSecurityInfoW failed (Win32 1),属于「该根不支持 Windows ACL 沙箱」,而不是可修缺陷。9P/SMB 重定向器不提供 Windows 安全描述符,读取当前安全信息会返回 ERROR_INVALID_FUNCTION,而 Windows 平台上沙箱只有一个 windows-acl 后端、没有 UNC 分支。规避有两条:把该会话切成 read-only(只读模式不读 ACL),或改成盘符路径(先在资源管理器把网络位置映射成盘符)。

相关术语

workspace-write
workspace-write 是 DeepSeek Harness 沙箱的一种文件策略,只允许普通工具进程写工作区(以及显式授权的路径),写工作区之外的位置会被拒绝。它在 Windows 上由 @deepseek-ai/dsh-sandbox-windows-acl 实现,用受限令牌加 ACL 授权来落地,因此授权本身失败时表现为「命令还没跑就报错」。— https://github.com/deepseek-ai/deepseek-harness/discussions/9175
受限令牌(restricted token)
受限令牌是 Windows 上通过 CreateRestrictedToken 生成、附带了 restricting SID 列表的访问令牌,内核在做访问检查时要求对象的 ACL 同时满足正常组与 restricting 列表两趟检查,任一趟不过就拒绝。DeepSeek Harness 用它在 Windows 上把子进程的写能力压到工作区范围内。— https://github.com/deepseek-ai/deepseek-harness/discussions/9549
SetNamedSecurityInfoW
SetNamedSecurityInfoW 是 Windows 提供的按名字设置对象安全描述符的 API,可以一次同时改 DACL、SACL、属主等部件。DSH 的 grantWrite 用它一次写入 capability SID 允许 ACE、world 删除拒绝 ACE 和 Low 完整性标签,因此任何一项前提不满足(例如缺 WRITE_OWNER)都会整体返回 ERROR_ACCESS_DENIED。— https://github.com/deepseek-ai/deepseek-harness/discussions/9818
STATUS_DLL_INIT_FAILED(0xC0000142)
STATUS_DLL_INIT_FAILED 是 Windows 的 NTSTATUS 退出码 0xC0000142,表示进程在加载器初始化阶段就失败了,通常意味着进程没有被授予创建控制台或所需内核对象的能力,因此连 main 都没进。DeepSeek Harness 在受限沙箱里遇到它,往往与匿名管道创建被拒或缺少可继承控制台有关。— https://github.com/deepseek-ai/deepseek-harness/discussions/9857

来源