Windows: sandbox setup_marker.json written without an ACE for the invoking user in non-interactive exec, so setup repeats forever (orchestrator_helper_incomplete)
Summary
On Windows, when codex exec runs non-interactively with a CODEX_HOME that has no existing sandbox state, the sandbox setup helper writes .sandbox\setup_marker.json with an ACL that contains no entry for the invoking user. The marker therefore cannot be read back on any later run, so every subsequent invocation reports the marker as missing/incompatible, re-runs full sandbox setup, and the exec fails before the command runs:
exec_command failed for `"C:\Program Files\PowerShell\7\pwsh.exe" -NoProfile -Command "..."`:
CreateProcess { message: "Rejected(\"Failed to create unified exec process:
orchestrator_helper_incomplete: setup helper exited successfully before setup completed\")" }
The same machine runs the same command successfully from the interactive app, because the interactive setup writes a marker the user can read. That asymmetry appears to be the whole defect.
Environment
- Windows 11
- codex-cli installed via
npm i -g @openai/codex - Reproduced on 0.146.0-alpha.9.2, 0.149.1 and 0.150.1
[windows] sandbox = "elevated",sandbox_mode = "workspace-write"- Non-admin user account; UAC prompt for
codex-windows-sandbox-setup.exeaccepted each time
Reproduction
- Point
CODEX_HOMEat a directory with no existing.sandboxstate:
````
set CODEX_HOME=D:\work\codex_home\job-0001
- Run a trivial exec non-interactively (stdin-fed prompt, no TTY):
```
codex exec --skip-git-repo-check -s workspace-write -c approval_policy=never --ephemeral -
cmd /c echo OK`.
with a prompt asking the model to run one shell command, e.g.
- Accept the UAC prompt. The run fails with
orchestrator_helper_incomplete. - Run the identical command again against the same
CODEX_HOME. Setup runs again and it fails again — indefinitely.
Evidence
%CODEX_HOME%\.sandbox\sandbox.<date>.log shows setup starting, completing its work, and finishing:
codex.exe] sandbox setup required: sandbox setup marker missing or incompatible
ensuring sandbox users offline=CodexSandboxOffline online=CodexSandboxOnline
firewall rule configured name=codex_sandbox_offline_block_loopback_udp ...
WFP setup succeeded for CodexSandboxOffline with 12 installed filters
granting write ACE to <temp dir> for sandbox group and capability SID
applied deny ACE to protect <workdir>\.git
codex-windows-sandbox-setup.exe] setup binary completed
Setup takes roughly 20 seconds, and the model's shell call is attempted while it is still running — which matches the wording "setup helper exited successfully before setup completed".
The marker it leaves behind is unreadable to the user who launched it:
> icacls "%CODEX_HOME%\.sandbox\setup_marker.json"
setup_marker.json: Access is denied.
Successfully processed 0 files; Failed processing 1 files
Compare the marker written by the interactive app, same machine, same user:
> icacls "C:\Users\<user>\.codex\.sandbox\setup_marker.json"
setup_marker.json NT AUTHORITY\SYSTEM:(F)
BUILTIN\Administrators:(F)
<MACHINE>\<user>:(F)
Granting the missing read ACE by hand changes the failure message on the next run fromsandbox setup marker missing or incompatible tosandbox users missing or incompatible with marker version,
i.e. the marker then parses but no longer matches the sandbox accounts — consistent with setup having rotated those accounts on the run that wrote the unreadable marker. The grant does not survive, because the next setup writes a fresh marker with the same restrictive ACL.
Across four days of logs on this machine, junction: failed to create <profile>\.codex\.sandbox\cwd: Access is denied. (os error 5) appears 83 times and never once succeeds; the sandbox accounts have no loaded user profile, so that path resolves under C:\Users\Default, which is read-only for non-admins. This may be a second symptom of the same "setup runs as a principal whose view of the user differs" problem.
Expected
A marker written by sandbox setup should be readable by the user who invoked codex, so that a second run can reuse the completed setup instead of repeating it.
Actual
The marker is unreadable to that user, so setup repeats on every invocation and non-interactive exec can never obtain a shell.
Ruled out during investigation
Each of these was tested and made no difference: PowerShell 7 from the Store/MSIX vs. the MSI build (byte-identical error text); a full reboot; upgrading 0.149.1 → 0.150.1; downgrading to 0.146.0-alpha.9.2; third-party antivirus disabled; and closing the interactive app entirely, so nothing else was competing for the machine-wide sandbox accounts.
Impact
Interactive use is unaffected. Automated/headless codex exec — CI, scheduled jobs, any orchestration that gives each run its own CODEX_HOME — cannot use the shell tool at all on Windows.
4 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Related, and likely the same root cause reached by a different entry point: #40217 reports the identical
sandbox setup marker missing or incompatible/orchestrator_helper_incompletecycle, there from the VS Code extension's app-server path rather than fromcodex exec. The common factor in both reports is sandbox setup being invoked from a non-interactive host process.One thing worth adding for whoever picks this up: the workaround in #40217 — granting the invoking user Full Control on
setup_marker.jsonby hand — is not a general one. It repairs a marker at a fixed path, so it can help a single long-livedCODEX_HOME. Any orchestration that gives each run its ownCODEX_HOMEhas no stable marker to repair, and the next setup writes a fresh marker with the same restrictive ACL regardless.Closing this — the cause was on my side, not in codex
I found the real cause and it is my harness, not this project. Posting the full
mechanism rather than deleting the issue, because the report above is wrong in a
specific and reproducible way and anyone who finds it by searching the same error
string deserves the correction.
What actually happened
My launcher builds the child environment for
codex execfrom an explicitallowlist (a deliberate no-secret-inheritance measure). That allowlist did not
include
USERNAME.codexresolves the invoking end-user from the environment — its own stringsinclude
failed to determine current user from environment:,--user or --current-user is required, and--codex-home is required with --user. WithUSERNAMEabsent, the elevatedsetup helper stamped the end-user ACE on
<CODEX_HOME>\.sandboxas:where the same slot in a normally-launched home carries:
Same slot, same access mask, same inheritance flags — only the trustee differs.
My account is in
Administrators, but a non-elevated token carries that SID asGroup used for deny only(whoami /groups, Medium Mandatory Level), and adeny-only SID never grants access. So
setup_marker.jsonwas unreadable to thevery orchestrator that had just requested it →
sandbox setup marker missing or→ setup re-runs → exits 0 with nothing to do →incompatible
orchestrator_helper_incomplete.Fix and acceptance
Adding
USERNAME(andUSERDOMAIN) to my launcher's environment allowlist fixesit. Measured, not inferred: a canary job returned
SHELL_CANARY_OK, against abyte-identical prompt run 30 minutes earlier that returned
orchestrator_helper_incomplete.Corrections to the report above
USERNAMEin thechild environment is. Non-interactive execution correlated with it only because
that is how my harness spawns codex.
app" — not the same binary.** The interactive app here is the MSIX
OpenAI.CodexBetabuild; the failing lane is the npm CLI atAppData\Roaming\npm\node_modules\@openai\codex. The asymmetry I described wasreal but I attributed it to interactive-vs-headless when the environment was
the actual difference.
through the same broken harness, so that list is not evidence about the
versions.
the same root cause reached by a different entry point". I have no evidence for
that, and since my own case turned out to be environmental, that guess should
not be allowed to steer someone else's report. Treating it as unrelated until
someone shows otherwise.
junction: failed to create ...\.codex\.sandbox\cwd: Access is deniedobservation is unexplained.** It may or may not share this cause; I did not
test it and am not claiming it.
Two things you may still want, offered without any claim that they are bugs
ACE the invoking token provably cannot use and continues. Failing loudly at
that point — the binary already has the string for it — would have turned four
days of investigation into one line of output.
completed` is accurate but points away from the cause. "Setup marker written
but not readable by <user>" would name it.
One diagnostic note in case it saves someone else the time: with
--ephemeral,the per-job
.sandboxdirectory is removed at exit (its mtime matched the job'send time to the millisecond). Every post-mortem I ran found an empty directory
and concluded setup had never started, which is why the ACL cause kept getting
refuted by its own evidence being deleted. The marker is only observable while
the job is still running.
Apologies for the noise, and thanks for the tracker.
Closing as not a codex defect -- root cause was my own launcher stripping USERNAME from the child environment. Full mechanism, fix and acceptance measurement in the comment above.