DeepSeek Harness 沙箱命令全失败:workspace-write 的 temp 根与管道 stdio
在 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 做的包含关系,不是字符串前缀,所以下面这些工作区都会命中:
- 工作区 =
C:\Users\<你>(TEMP是C:\Users\<你>\AppData\Local\Temp,被包含); - 工作区 =
C:\Users(整个用户目录树); - 工作区 =
C:\(盘根,几乎所有临时目录都在里面); - 任何把
TMP/TEMP环境变量指到工作区内部的配置。
讨论里同时给了一个重要的下一层提示:这条规则本身是有意的 capability 隔离,不是缺陷;真正可改进的是失败时的降级方式与报错可操作性——现在的报错只告诉你「必须在工作区外」,没告诉你改哪个变量(#9175)。
DeepSeek Harness 编号处置:把 temp 根搬出工作区
修法是把 TMP / TEMP 指到工作区外的固定目录,而不是去改工作区。 步骤如下:
- 先确认当前 temp 根落在哪。在非受限的 PowerShell 里执行:
[System.IO.Path]::GetTempPath()
# → 例如 C:\Users\<你>\AppData\Local\Temp\
- 把结果与工作区路径做包含判断。若工作区是
C:\Users\<你>、而输出落在它下面,即命中本条。工作区可以用设置 → 工作区里显示的路径核对。 - 建一个明确在工作区外的临时目录,并把它设为用户级
TMP/TEMP:
New-Item -ItemType Directory -Force D:\dsh-temp | Out-Null
setx TMP "D:\dsh-temp"
setx TEMP "D:\dsh-temp"
- 完全退出 DSH 再重开(
setx只影响新进程,正在跑的宿主不会继承)。 - 重开会话后在
workspace-write下跑一条最小命令验证:
pwsh -Command "echo hi"
# → 预期输出 hi,不再报 must be outside the workspace
- 回滚:把
TMP/TEMP删回用户默认即可,setx TMP ""后重启会话。
一个必须记住的连带现象:把工作区设成
C:\Users这类系统属主目录时,改完TMP只是让它从「temp 根报错」走到「下一层 ACL 授权失败」,因为C:\Users及其兄弟目录的属主与 DACL 都不满足grantWrite前提。这类工作区建议整体换掉,别在原地硬修(#9175)。
DSH插件 会话内临时目录失效:同一会话所有命令持续失败、不自愈
还有一种「命令执行器整体失效」不是环境变量引起的,而是沙箱缓存了一个已经消失的临时目录。 症状是:会话开头几条命令还正常,之后所有命令(含只读命令)都失败,重启 DSH 才能恢复(#9322)。
据社区逐行核对源码,链路是这样的:
- 沙箱在会话内为临时能力建了一张缓存表,命中缓存后直接返回,没有再做一次「目录是否还在」的复验;
- 于是缓存里的那个已失效路径被原样下发给 runner;
- runner 在启动时对
--temp目录做必需性检查,找不到目录即退出,退出码 127 并在 stderr 带windows-acl-run:前缀; - 缓存表只在 provider dispose 时清空,所以同一会话内不会自愈。
关于「目录为什么会消失」,讨论里如实标注为未定论——代码里没有删除这条路径的地方,最可能是 DSH 之外的进程(清理工具、杀软隔离、临时目录清理策略)动了它(#9322)。
处置办法(按代价从小到大):
- 重启 DSH——会重建新的临时路径与 SID,一步恢复;
- 应急时把该会话切成
read-only(只读档不建可写根,也不依赖那份临时能力); - 长期避免:把工作区与系统临时目录清理策略错开,别让第三方清理器扫到 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)。
处置步骤:
- 确认工作区根是不是
\\wsl.localhost\...、\\wsl$\...、网络共享或云盘挂载点; - 切该会话到
read-only,预期 PTC 立即恢复(只读档不读 ACL); - 或先在资源管理器把这个网络位置映射成盘符(
Z:\...),再用盘符路径重建工作区; - 两者都不想动时,改用
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 工具调用永久卡死。
判读规则与替代命令(实测可用的替代路线):
- 先记住一条判读规则:沙箱内 CIM 命令的空结果和 0 值都不算证据,别据此判断磁盘满、网卡断、进程不存在。
- 网络连接改用
netstat -ano替代Get-NetTCPConnection。 - 进程与服务改用
Get-Process、Get-Service替代Get-CimInstance Win32_Process/Win32_Service。 - 磁盘容量改用
[System.IO.DriveInfo]::GetDrives()替代Get-PSDrive的 Used/Free。 - 网卡与路由改用
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 编号排查:三步确认是管道问题而不是命令问题
先做对照,再改命令。 三步:
- 找到一条失败的命令,把它的输出重定向去掉,改成直接打屏,重跑。若立刻正常 → 定性为管道捕获问题。
- 再确认失败时的真实退出码。在 DSH 会话里跑
cmd /c "pwsh -Command \"echo hi\" > %TEMP%\o.txt & echo exit=%errorlevel%",预期看到exit=-1073741502(即0xC0000142)。 - 检查那个被重定向的文件:
Get-Item %TEMP%\o.txt | Select Length,长度是 0 即「输出被静默丢弃」这一格。
DSH插件 处置办法:三条可用路线
- 首选——不捕获输出。让命令把结果直接写进会话日志或写进工作区文件,用
read工具去看,不要在受限模式里做重定向与赋值捕获。 - 跑测试/构建类命令时用
inherit或ignorestdio,别用pipe。据讨论中社区引用的包内 README,这是文档化的已知边界:受限令牌下命名管道客户端要请求写访问,而 restricting SID 没有被授予该权限,这一步是 Win32 默认安全描述符模板决定的,改令牌默认 DACL 也解决不了命名管道(#9549)。 - 需要完整跑通 CI 类脚本时,把这一步放到不受限的外层执行(例如在宿主终端里跑),DSH 只负责读产物。
顺带一个省时间的区分:
spawn EPERM有的是「受限进程起孙进程的管道问题」(本文这条),有的是「沙箱 runner 自己没起来」——后者的特征是 stderr 带windows-acl-run:且退出码 127。看到这个前缀就别在业务命令上找了(#9549)。
DeepSeek Harness ACL 整树重写:宿主无响应但进程不崩
一句话结论:当某个已授权根缺少「三件套」里的任意一项时,沙箱会退化成对整棵树做一次急切的 SetNamedSecurityInfoW,而这次调用是在宿主进程内同步执行的——期间事件循环被占满,127.0.0.1:3080 自然无响应,所有会话停止写入,但进程不会崩、也不会有任何报错日志。 这是本族里唯一「看起来像死机」的一条(#9818)。
据社区核对源码,判据与放大点如下:
- 幂等短路要求同一个根同时具备:capability 允许 ACE、
Everyone:(CI)(DENY)(DC)的 world 删除拒绝,以及 Low 完整性标签; - 缺任意一条即走合并路径
mergeAndApply,对整棵树做急切传播; - 调用点在宿主进程内且是同步的,没有让出事件循环;
- 放大点在于升级:较早版本只授过 capability ACE,world 删除拒绝与 Low 标签是后续引入的,因此升级用户会在下一次供给时触发一次整树重写。
讨论中给了一个量级参考:D:\projects 这种体量的工作区,整树传播实测约 4490 秒(约 75 分钟),期间宿主全程无响应(#9818)。
DSH插件 编号处置与预防
- 先确认是不是它:接口无响应期间,
Get-Process看宿主进程是否仍在、CPU 是否被单核打满;配合icacls "<工作区>"观察是否正在被批量写入标签。 - 短期恢复:等它跑完,或重启宿主。不要在这期间反复点 UI 触发新的供给。
- 预防——先补齐三条件让幂等短路命中:确认根上同时存在 capability 允许 ACE、world 删除拒绝与 Low 标签,缺的那个补齐后再把工作区交给 DSH;这套 Low 标签自身的副作用与撤销方法,另见 DeepSeek Harness 沙箱 Low 完整性标签。
- 避免让单个工作区过大;大工作区建议拆成多个较小的工作区分别连接。
- 版本提示:据社区核对,
0.2.0-rc.2与后续 alpha 的两个 ACL 包哈希一致,该行为尚未修;「把整树传播移出事件循环」属于核心侧改动,插件层只能观测、无法规避(#9818)。
DSH插件 排查注意事项
五条症状同属「Windows ACL 沙箱前提不满足」一族,但触发点各不相同,按报错文本分流最快,别一上来就改 ACL。 九条要点:
- 先读报错文本:
must be outside the workspace是 temp 根位置;windows-acl-run:+ exit 127 是 runner 启动失败;spawn EPERM是管道 stdio;0xC0000142是子进程加载器初始化;Win32 1是 UNC 根不支持;只有Win32 5才是经典的 ACL 授权失败。 - 文件工具正常、只有 shell 失败,几乎可以确定问题在沙箱初始化而非命令本身——文件工具走的是另一条路径(#9175)。
TMP/TEMP是唯一杠杆:改完必须完全退出 DSH 再重开,setx不影响已在运行的宿主。- 不要用
C:\、C:\Users、用户主目录当工作区:这三类会让 temp 根与 ACL 授权两条前提同时不满足,属于自找麻烦。 - 重定向与赋值捕获是禁忌:受限模式里凡是被捕获的输出都可能变成 0 字节或直接触发
0xC0000142。 inherit/ignorestdio 是文档化边界内的解法,不是绕过安全控制——改令牌默认 DACL 解决不了命名管道(#9549)。- 宿主无响应时不要急着重启:先看是不是整树重写;75 分钟量级的中途重启会让下一次供给从头再来。
- 沙箱内的 CIM 结果不可信:空值与 0 值要当成「被拒绝」而不是「真的没有」(#9272)。
- 修复状态如实:截至
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 报 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 退出,根因是受限令牌下匿名管道创建被拒。当 Python 的 print 被 stdout 重定向、或者你用 $x = pwsh -c ... / 2>&1 / > 去接住输出时,Windows 需要为子进程建立匿名管道,而低完整性受限令牌没有创建它所需的权限,于是子进程要么加载失败(STATUS_DLL_INIT_FAILED,0xC0000142),要么输出被静默丢弃、只留下一个 0 字节文件。最快的规避是在受限模式里避免捕获原生命令输出,让它直接打到会话日志。
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 无响应、会话不再写入,大概率是撞上了 ACL 整树重写。当某个已授权根缺少三件套(capability 允许 ACE、Everyone 的 world 删除拒绝 ACE、Low 完整性标签)里的任意一项时,沙箱会退化成对整棵树做一次急切的 SetNamedSecurityInfoW,而这一步是在宿主进程内同步执行的,事件循环被占满期间 HTTP 服务自然不响应。实测过一个 D:\projects 量级的工作区整树传播耗时约 75 分钟。恢复办法是等它跑完或重启宿主,长期做法是把工作区根先补齐三条件,让幂等短路命中。
把 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
来源
- #9175 — Windows:工作区包含 TEMP 时,workspace-write 下命令执行器整体失效· deepseek-ai(GitHub Discussions)
- #9322 — Windows 沙箱临时目录缺失后,同一会话内所有命令持续失败· deepseek-ai(GitHub Discussions)
- #9857 — Windows:workspace-write 沙箱捕获原生命令输出时子进程 0xC0000142 且输出静默丢失· deepseek-ai(GitHub Discussions)
- #9549 — Windows ACL 沙箱下管道 stdio spawn 孙进程报 spawn EPERM· deepseek-ai(GitHub Discussions)
- #9818 — Windows ACL 给大工作区整树重写 ACL,宿主事件循环被焊死· deepseek-ai(GitHub Discussions)
- #9315 — Windows ACL 沙箱在 UNC/9P 工作区根上 GetNamedSecurityInfoW 失败· deepseek-ai(GitHub Discussions)
- #9272 — Windows 受限令牌沙箱内 CIM/WMI 整体不可用· deepseek-ai(GitHub Discussions)