Bug report: Windows windows-acl sandbox dies permanently for a session when its cached private temp dir disappears #6483
Unanswered
Flen-Plnens
asked this question in
Q&A
Replies: 1 comment
|
I verified your report against master (c291e79, 2026-09-10). It holds, line Verified facts:
Status and workaround:
|
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Product:
@deepseek-ai/dsh0.1.5-rc.2 (npmnext;latestat the time of writing is 0.1.5-rc.1)Host: Windows 10, Node v24.19.0, x64
Backend:
windows-acl(restricted-token runner) — the chain's sole win32 candidateSeverity: high for Windows users — a session loses all confined execution until the DSH process restarts
1. Symptom
Every confined
workspace-writecommand in an affected session fails with:(Structured error:
name: "SandboxUnavailableError",code: "SANDBOX_UNAVAILABLE".)Two things make this worse than it looks:
normally — the ACL runner rewrites
TMP/TEMPto a capability-granted private dir and enforces theworkspace boundary. The error text is therefore misleading, and it invites the model to escalate to
danger-full-access(which is exactly what happened in practice — see §4).private temp dir, across 25 minutes. The session is confined-execution-dead until the DSH process
restarts.
2. Root cause
@deepseek-ai/dsh-sandbox-localcaches one private temp directory per(sessionId, workspaceRoot)forthe provider's lifetime and hands that path to the runner on every confined call — without ever
re-validating that the directory still exists:
The runner then fails closed on its very first pre-flight check
(
dsh-sandbox-windows-acl/lib/runner.js,requireDirectory→existsSync), printswindows-acl-run: --temp is not an existing directory: …and exits127, whichRUNNER_FAILURE_RULES["windows-acl"]maps toSandboxUnavailableError.The deletion itself comes from DSH's own cleanup: in the whole installed tree the only code that removes
these directories is
sandbox-local'sremoveTempDir(), called fromrevokeAclGrants()(provider dispose / cordis effect teardown), andmaterializeAclGrant().Once such a removal happens while the provider instance (and therefore its
tempCapabilitiesmap)survives, the session is stuck. We could not pin down from the artifacts which of those two paths
fires first — that is the one open question; the "cache is never re-validated" half is proven by the
identical stale path being reused for 25 minutes.
3. Evidence
3.1 The backend works on this host (same process, minutes apart)
Current session, confined
pwshcommand:S-1-4-x-y-1is exactlytempWriteSid(privateTempDir)fromdsh-sandbox-windows-acl(tempWriteSid=S-1-4-<a>-<b>-1). Enforcement is live too:Roughly 40 confined commands in that session succeeded with zero runner failures.
3.2 Two affected sessions, months apart (so not a 0.1.5 regression)
Logs live at
$DSH_HOME/sessions/<cwd-slug>/<session>/session.v3.jsonl.zstd. Note: they aremulti-frame zstd —
zlib.zstdDecompressSync()(and the streaming API) decode only the firstframe, so a naive search finds nothing. Decoding all frames:
--tempin failuressession-ed1b02bb-c416-4967-97ef-a903b1c371c2D:\renpy-projects…\Temp\dsh-am0JWFsession-031541e1-92d1-411b-989c-97ca0f3a9ea2D:\GL2…\Temp\dsh-Xk3Q7DIn
session-ed1b02bb, the last healthy confined command is at 09:42:05; from 09:46:49 on, noconfined
workspace-writecommand ever succeeds again, and all 6 failures carry the same path.3.3 The "it works again afterwards" is escalation, not recovery
The successful commands interleaved with the failures are unconfined — the model escalated, with the
user approving each time:
{"type":"approval/asked","seq":1249,"toolName":"pwsh", "reason":"escalate sandbox to danger-full-access: The workspace-write sandbox runner is unusable (its temp directory no longer exists), so this Ren'Py lint check cannot run any other way; …"}(
approval/askedwith that reason appears repeatedly: seq 1249, 1405, 1441, 1501, 1508, 1515.)So an affected session silently degrades to
danger-full-access— the opposite of the intendedfail-closed outcome, and it makes agents report "the sandbox is broken" to the user.
3.4 Ruled out
%TEMP%: Storage Sense is disabled(
HKCU\…\StorageSense\Parameters\StoragePolicy\01 = 0, no policy keys), and unrelated temp files from08:45 are still present while the sandbox dirs vanished.
mkdtemp:fs.mkdtempSync()returns distinct random suffixes here(4/4 distinct);
crypto.randomBytesis fine. So a constant stale path cannot be re-creation.rmSync/rmdiranywhere indsh-sandbox-windows-acl;dsh-acl-locksholds lock files only.4. Suggested fix
Re-validate the cached capability before returning it (this is the local hotfix we are running, marked
[dsh-hotfix sandbox-local]):(
existsSyncis already imported in that module.) A missing directory is then simply re-materializedwith a fresh path/SID instead of poisoning the session.
Two secondary suggestions that would have shortened this diagnosis a lot:
"no sandbox backend is usable on this host"; the current text tells the model to switch to
danger-full-access, and models comply. Something likeSANDBOX_RUNNER_FAILED(state: the backend exists but this invocation's confinement could not beestablished; retry/rebuild, do not escalate) would prevent both the misleading escalation and the
"sandbox is broken" reports.
existsSyncfailed.requireDirectorycollapsesENOENTandEACCES/EPERMinto onemessage (Node's
existsSyncreturnsfalsefor any stat error). Reporting the actualstatSyncerror code would separate "deleted" from "present but unreadable" in the field.
5. Environment / repro notes
failing command in a turn is the one that surfaces it.
disappear between
materializeAclGrant()and the runner'sexistsSync(or the cached entry tooutlive the directory). Deleting the session's private temp dir out-of-band and then issuing a
confined command reproduces the identical error text, which is how the hotfix above was validated.
All reactions